From 087d5f0f64e7b81571800a6da565395a8aa0b560 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 13 Feb 2024 09:42:07 +0000 Subject: [PATCH 001/195] Change to vector.new from {x=...}, relocate movement code to own function for future changes --- mods/ENTITIES/mcl_minecarts/functions.lua | 58 +-- mods/ENTITIES/mcl_minecarts/init.lua | 417 +++++++++++----------- 2 files changed, 232 insertions(+), 243 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 1792e9252..ba2eff168 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -10,9 +10,17 @@ end function mcl_minecarts:velocity_to_dir(v) if math.abs(v.x) > math.abs(v.z) then - return {x=mcl_minecarts:get_sign(v.x), y=mcl_minecarts:get_sign(v.y), z=0} + return vector.new( + mcl_minecarts:get_sign(v.x), + mcl_minecarts:get_sign(v.y), + 0 + ) else - return {x=0, y=mcl_minecarts:get_sign(v.y), z=mcl_minecarts:get_sign(v.z)} + return vector.new( + 0, + mcl_minecarts:get_sign(v.y), + mcl_minecarts:get_sign(v.z) + ) end end @@ -69,8 +77,8 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) local left_check, right_check = true, true -- Check left and right - local left = {x=0, y=0, z=0} - local right = {x=0, y=0, z=0} + local left = vector.new(0, 0, 0) + local right = vector.new(0, 0, 0) if dir.z ~= 0 and dir.x == 0 then left.x = -dir.z right.x = dir.z @@ -124,44 +132,14 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) end -- Backwards if not old_switch then - cur = mcl_minecarts:check_front_up_down(pos, { - x = -dir.x, - y = dir.y, - z = -dir.z - }, true, railtype) + cur = mcl_minecarts:check_front_up_down(pos, vector.new( + -dir.x, + dir.y, + -dir.z + ), true, railtype) if cur then return cur end end - return {x=0, y=0, z=0} -end - -local plane_adjacents = { - vector.new(-1,0,0), - vector.new(1,0,0), - vector.new(0,0,-1), - vector.new(0,0,1), -} - -function mcl_minecarts:get_start_direction(pos) - local dir - local i = 0 - while (not dir and i < #plane_adjacents) do - i = i+1 - local node = minetest.get_node_or_nil(vector.add(pos, plane_adjacents[i])) - if node ~= nil - and minetest.get_item_group(node.name, "rail") == 0 - and minetest.get_item_group(node.name, "solid") == 1 - and minetest.get_item_group(node.name, "opaque") == 1 - then - dir = mcl_minecarts:check_front_up_down(pos, vector.multiply(plane_adjacents[i], -1), true) - end - end - return dir -end - -function mcl_minecarts:set_velocity(obj, dir, factor) - obj._velocity = vector.multiply(dir, factor or 3) - obj._old_pos = nil - obj._punched = true + return vector.new(0,0,0) --{x=0, y=0, z=0} end diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 89ae44349..5a46f97c2 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -305,9 +305,193 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local passenger_attach_position = vector.new(0, -1.75, 0) + local function do_movement_step( self ) + local vel = self.object:get_velocity() + local pos, rou_pos, node = self.object:get_pos() + local update = {} + + local dir, last_switch = nil, nil + if not pos then + pos = self.object:get_pos() + end + if self._old_pos and not self._punched then + local flo_pos = vector.floor(pos) + local flo_old = vector.floor(self._old_pos) + if vector.equals(flo_pos, flo_old) and (not has_fuel) then + return + -- Prevent querying the same node over and over again + end + + if not rou_pos then + rou_pos = vector.round(pos) + end + local rou_old = vector.round(self._old_pos) + if not node then + node = minetest.get_node(rou_pos) + end + local node_old = minetest.get_node(rou_old) + + -- Update detector rails + if node.name == "mcl_minecarts:detector_rail" then + local newnode = {name="mcl_minecarts:detector_rail_on", param2 = node.param2} + minetest.swap_node(rou_pos, newnode) + mesecon.receptor_on(rou_pos) + end + if node_old.name == "mcl_minecarts:detector_rail_on" then + local newnode = {name="mcl_minecarts:detector_rail", param2 = node_old.param2} + minetest.swap_node(rou_old, newnode) + mesecon.receptor_off(rou_old) + end + -- Activate minecart if on activator rail + if node_old.name == "mcl_minecarts:activator_rail_on" and self.on_activate_by_rail then + self:on_activate_by_rail() + end + end + + -- Stop cart if velocity vector flips + if self._old_vel and self._old_vel.y == 0 and + (self._old_vel.x * vel.x < 0 or self._old_vel.z * vel.z < 0) then + self._old_vel = {x = 0, y = 0, z = 0} + self._old_pos = pos + self.object:set_velocity(vector.new()) + self.object:set_acceleration(vector.new()) + return + end + self._old_vel = vector.new(vel) + + if self._old_pos then + local diff = vector.subtract(self._old_pos, pos) + for _,v in ipairs({"x","y","z"}) do + if math.abs(diff[v]) > 1.1 then + local expected_pos = vector.add(self._old_pos, self._old_dir) + dir, last_switch = mcl_minecarts:get_rail_direction(pos, self._old_dir, ctrl, self._old_switch, self._railtype) + + if vector.equals(dir, {x=0, y=0, z=0}) then + dir = false + pos = vector.new(expected_pos) + update.pos = true + end + break + end + end + end + + if vel.y == 0 then + for _,v in ipairs({"x", "z"}) do + if vel[v] ~= 0 and math.abs(vel[v]) < 0.9 then + vel[v] = 0 + update.vel = true + end + end + end + + local cart_dir = mcl_minecarts:velocity_to_dir(vel) + local max_vel = mcl_minecarts.speed_max + if not dir then + dir, last_switch = mcl_minecarts:get_rail_direction(pos, cart_dir, ctrl, self._old_switch, self._railtype) + end + + local new_acc = {x=0, y=0, z=0} + if vector.equals(dir, {x=0, y=0, z=0}) and not has_fuel then + vel = {x=0, y=0, z=0} + update.vel = true + else + -- If the direction changed + if dir.x ~= 0 and self._old_dir.z ~= 0 then + vel.x = dir.x * math.abs(vel.z) + vel.z = 0 + pos.z = math.floor(pos.z + 0.5) + update.pos = true + end + if dir.z ~= 0 and self._old_dir.x ~= 0 then + vel.z = dir.z * math.abs(vel.x) + vel.x = 0 + pos.x = math.floor(pos.x + 0.5) + update.pos = true + end + -- Up, down? + if dir.y ~= self._old_dir.y then + vel.y = dir.y * math.abs(vel.x + vel.z) + pos = vector.round(pos) + update.pos = true + end + + -- Slow down or speed up + local acc = dir.y * -1.8 + local friction = 0.4 + local ndef = minetest.registered_nodes[minetest.get_node(pos).name] + local speed_mod = ndef and ndef._rail_acceleration + + acc = acc - friction + + if has_fuel then + acc = acc + 0.6 + end + + if speed_mod and speed_mod ~= 0 then + acc = acc + speed_mod + friction + end + + new_acc = vector.multiply(dir, acc) + end + + self.object:set_acceleration(new_acc) + self._old_pos = vector.new(pos) + self._old_dir = vector.new(dir) + self._old_switch = last_switch + + -- Limits + for _,v in ipairs({"x","y","z"}) do + if math.abs(vel[v]) > max_vel then + vel[v] = mcl_minecarts:get_sign(vel[v]) * max_vel + new_acc[v] = 0 + update.vel = true + end + end + + + if update.pos or self._punched then + local yaw = 0 + if dir.x < 0 then + yaw = 0.5 + elseif dir.x > 0 then + yaw = 1.5 + elseif dir.z < 0 then + yaw = 1 + end + self.object:set_yaw(yaw * math.pi) + end + + if self._punched then + self._punched = false + end + + if not (update.vel or update.pos) then + return + end + + local anim = {x=0, y=0} + if dir.y == -1 then + anim = {x=1, y=1} + elseif dir.y == 1 then + anim = {x=2, y=2} + end + self.object:set_animation(anim, 1, 0) + + self.object:set_velocity(vel) + if update.pos then + self.object:set_pos(pos) + end + end + function cart:on_step(dtime) hopper_take_item(self, dtime) + local vel = self.object:get_velocity() + local pos, rou_pos, node = self.object:get_pos() + local update = {} + + -- Controls local ctrl, player = nil, nil if self._driver then player = minetest.get_player_by_name(self._driver) @@ -319,17 +503,23 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o return end end + + -- Give achievement when player reached a distance of 1000 nodes from the start position + if vector.distance(self._start_pos, pos) >= 1000 then + awards.unlock(self._driver, "mcl:onARail") + end end - local vel = self.object:get_velocity() - local update = {} - if self._last_float_check == nil then - self._last_float_check = 0 - else - self._last_float_check = self._last_float_check + dtime + -- Go faster when punched + if self._punched then + vel = vector.add(vel, self._velocity) + self.object:set_velocity(vel) + self._old_dir.y = 0 + elseif vector.equals(vel, {x=0, y=0, z=0}) and (not has_fuel) then + return end - local pos, rou_pos, node = self.object:get_pos() + -- Drop minecart if it collides with a cactus node local r = 0.6 for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then @@ -342,6 +532,17 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end end + -- Debug + local node = minetest.get_node(pos).name + local dist = 0 + if pos and self._old_pos then + dist = vector.distance(pos,self._old_pos) + end + if dist > 1.5 then + print("pos="..tostring(pos)..",dist="..tostring(dist)..",node="..tostring(node)..",old_pos="..tostring(self._old_pos)..",vel="..tostring(vel)) + end + -- Rail jumps can occur when dist > 1.5, because the cart can skip over a gap in track + -- Grab mob if math.random(1,20) > 15 and not self._passenger then if self.name == "mcl_minecarts:minecart" then @@ -366,6 +567,11 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end -- Drop minecart if it isn't on a rail anymore + if self._last_float_check == nil then + self._last_float_check = 0 + else + self._last_float_check = self._last_float_check + dtime + end if self._last_float_check >= mcl_minecarts.check_float_time then pos = self.object:get_pos() rou_pos = vector.round(pos) @@ -458,202 +664,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end end - if self._punched then - vel = vector.add(vel, self._velocity) - self.object:set_velocity(vel) - self._old_dir.y = 0 - elseif vector.equals(vel, {x=0, y=0, z=0}) and (not has_fuel) then - return - end - - local dir, last_switch, restart_pos = nil, nil, nil - if not pos then - pos = self.object:get_pos() - end - if self._old_pos and not self._punched then - local flo_pos = vector.floor(pos) - local flo_old = vector.floor(self._old_pos) - if vector.equals(flo_pos, flo_old) and (not has_fuel) then - return - -- Prevent querying the same node over and over again - end - - if not rou_pos then - rou_pos = vector.round(pos) - end - local rou_old = vector.round(self._old_pos) - if not node then - node = minetest.get_node(rou_pos) - end - local node_old = minetest.get_node(rou_old) - - -- Update detector rails - if node.name == "mcl_minecarts:detector_rail" then - local newnode = {name="mcl_minecarts:detector_rail_on", param2 = node.param2} - minetest.swap_node(rou_pos, newnode) - mesecon.receptor_on(rou_pos) - end - if node.name == "mcl_minecarts:golden_rail_on" then - restart_pos = rou_pos - end - if node_old.name == "mcl_minecarts:detector_rail_on" then - local newnode = {name="mcl_minecarts:detector_rail", param2 = node_old.param2} - minetest.swap_node(rou_old, newnode) - mesecon.receptor_off(rou_old) - end - -- Activate minecart if on activator rail - if node_old.name == "mcl_minecarts:activator_rail_on" and self.on_activate_by_rail then - self:on_activate_by_rail() - end - end - - -- Stop cart if velocity vector flips - if self._old_vel and self._old_vel.y == 0 and - (self._old_vel.x * vel.x < 0 or self._old_vel.z * vel.z < 0) then - self._old_vel = {x = 0, y = 0, z = 0} - self._old_pos = pos - self.object:set_velocity(vector.new()) - self.object:set_acceleration(vector.new()) - return - end - self._old_vel = vector.new(vel) - - if self._old_pos then - local diff = vector.subtract(self._old_pos, pos) - for _,v in ipairs({"x","y","z"}) do - if math.abs(diff[v]) > 1.1 then - local expected_pos = vector.add(self._old_pos, self._old_dir) - dir, last_switch = mcl_minecarts:get_rail_direction(pos, self._old_dir, ctrl, self._old_switch, self._railtype) - if vector.equals(dir, {x=0, y=0, z=0}) then - dir = false - pos = vector.new(expected_pos) - update.pos = true - end - break - end - end - end - - if vel.y == 0 then - for _,v in ipairs({"x", "z"}) do - if vel[v] ~= 0 and math.abs(vel[v]) < 0.9 then - vel[v] = 0 - update.vel = true - end - end - end - - local cart_dir = mcl_minecarts:velocity_to_dir(vel) - local max_vel = mcl_minecarts.speed_max - if not dir then - dir, last_switch = mcl_minecarts:get_rail_direction(pos, cart_dir, ctrl, self._old_switch, self._railtype) - end - - local new_acc = {x=0, y=0, z=0} - if vector.equals(dir, {x=0, y=0, z=0}) and not has_fuel then - vel = {x=0, y=0, z=0} - update.vel = true - else - -- If the direction changed - if dir.x ~= 0 and self._old_dir.z ~= 0 then - vel.x = dir.x * math.abs(vel.z) - vel.z = 0 - pos.z = math.floor(pos.z + 0.5) - update.pos = true - end - if dir.z ~= 0 and self._old_dir.x ~= 0 then - vel.z = dir.z * math.abs(vel.x) - vel.x = 0 - pos.x = math.floor(pos.x + 0.5) - update.pos = true - end - -- Up, down? - if dir.y ~= self._old_dir.y then - vel.y = dir.y * math.abs(vel.x + vel.z) - pos = vector.round(pos) - update.pos = true - end - - -- Slow down or speed up - local acc = dir.y * -1.8 - local friction = 0.4 - local ndef = minetest.registered_nodes[minetest.get_node(pos).name] - local speed_mod = ndef and ndef._rail_acceleration - - acc = acc - friction - - if has_fuel then - acc = acc + 0.6 - end - - if speed_mod and speed_mod ~= 0 then - acc = acc + speed_mod + friction - end - - new_acc = vector.multiply(dir, acc) - end - - self.object:set_acceleration(new_acc) - self._old_pos = vector.new(pos) - self._old_dir = vector.new(dir) - self._old_switch = last_switch - - -- Limits - for _,v in ipairs({"x","y","z"}) do - if math.abs(vel[v]) > max_vel then - vel[v] = mcl_minecarts:get_sign(vel[v]) * max_vel - new_acc[v] = 0 - update.vel = true - end - end - - -- Give achievement when player reached a distance of 1000 nodes from the start position - if self._driver and (vector.distance(self._start_pos, pos) >= 1000) then - awards.unlock(self._driver, "mcl:onARail") - end - - - if update.pos or self._punched then - local yaw = 0 - if dir.x < 0 then - yaw = 0.5 - elseif dir.x > 0 then - yaw = 1.5 - elseif dir.z < 0 then - yaw = 1 - end - self.object:set_yaw(yaw * math.pi) - end - - if self._punched then - self._punched = false - end - - if not (update.vel or update.pos) then - return - end - - - local anim = {x=0, y=0} - if dir.y == -1 then - anim = {x=1, y=1} - elseif dir.y == 1 then - anim = {x=2, y=2} - end - self.object:set_animation(anim, 1, 0) - - self.object:set_velocity(vel) - if update.pos then - self.object:set_pos(pos) - end - - -- stopped on "mcl_minecarts:golden_rail_on" - if vector.equals(vel, {x=0, y=0, z=0}) and restart_pos then - local dir = mcl_minecarts:get_start_direction(restart_pos) - if dir then - mcl_minecarts:set_velocity(self, dir) - end - end + do_movement_step(self) end function cart:get_staticdata() From d1550d9e84aaf8d9899a0e146d23aed3944f2c6c Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 13 Feb 2024 10:42:38 +0000 Subject: [PATCH 002/195] Change staticdata serialization (with migration from old data), disable debugging code used to investigate https://git.minetest.land/MineClone2/MineClone2/issues/2446 --- mods/ENTITIES/mcl_minecarts/init.lua | 50 +++++++++++++++++----------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 5a46f97c2..b1883af72 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -207,15 +207,22 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o _old_pos = nil, _old_vel = {x=0, y=0, z=0}, _old_switch = 0, - _railtype = nil, + _staticdata = { + railtype = nil, + }, } function cart:on_activate(staticdata, dtime_s) -- Initialize local data = minetest.deserialize(staticdata) if type(data) == "table" then - self._railtype = data._railtype - self._passenger = data._passenger + -- Migrate old data + if data._railtype then + data.railtype = data._railtype + data._railtype = nil + end + + self._staticdata = data end self.object:set_armor_groups({immortal=1}) @@ -231,13 +238,13 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) local pos = self.object:get_pos() - if not self._railtype then + if not self._staticdata.railtype then local node = minetest.get_node(vector.floor(pos)).name - self._railtype = minetest.get_item_group(node, "connect_to_raillike") + self._staticdata.railtype = minetest.get_item_group(node, "connect_to_raillike") end if not puncher or not puncher:is_player() then - local cart_dir = mcl_minecarts:get_rail_direction(pos, {x=1, y=0, z=0}, nil, nil, self._railtype) + local cart_dir = mcl_minecarts:get_rail_direction(pos, {x=1, y=0, z=0}, nil, nil, self._staticdata.railtype) if vector.equals(cart_dir, {x=0, y=0, z=0}) then return end @@ -290,7 +297,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local punch_dir = mcl_minecarts:velocity_to_dir(puncher:get_look_dir()) punch_dir.y = 0 - local cart_dir = mcl_minecarts:get_rail_direction(pos, punch_dir, nil, nil, self._railtype) + local cart_dir = mcl_minecarts:get_rail_direction(pos, punch_dir, nil, nil, self._staticdata.railtype) if vector.equals(cart_dir, {x=0, y=0, z=0}) then return end @@ -364,7 +371,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o for _,v in ipairs({"x","y","z"}) do if math.abs(diff[v]) > 1.1 then local expected_pos = vector.add(self._old_pos, self._old_dir) - dir, last_switch = mcl_minecarts:get_rail_direction(pos, self._old_dir, ctrl, self._old_switch, self._railtype) + dir, last_switch = mcl_minecarts:get_rail_direction(pos, self._old_dir, ctrl, self._old_switch, self._staticdata.railtype) if vector.equals(dir, {x=0, y=0, z=0}) then dir = false @@ -388,7 +395,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local cart_dir = mcl_minecarts:velocity_to_dir(vel) local max_vel = mcl_minecarts.speed_max if not dir then - dir, last_switch = mcl_minecarts:get_rail_direction(pos, cart_dir, ctrl, self._old_switch, self._railtype) + dir, last_switch = mcl_minecarts:get_rail_direction(pos, cart_dir, ctrl, self._old_switch, self._staticdata.railtype) end local new_acc = {x=0, y=0, z=0} @@ -533,15 +540,18 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end -- Debug - local node = minetest.get_node(pos).name - local dist = 0 - if pos and self._old_pos then - dist = vector.distance(pos,self._old_pos) + if false then + local node = minetest.get_node(pos).name + local dist = 0 + if pos and self._old_pos then + dist = vector.distance(pos,self._old_pos) + end + if dist > 1.5 then + print("pos="..tostring(pos)..",dist="..tostring(dist)..",node="..tostring(node)..",old_pos=".. + tostring(self._old_pos)..",vel="..tostring(vel)) + end + -- Rail jumps can occur when dist > 1.5, because the cart can skip over a gap in track end - if dist > 1.5 then - print("pos="..tostring(pos)..",dist="..tostring(dist)..",node="..tostring(node)..",old_pos="..tostring(self._old_pos)..",vel="..tostring(vel)) - end - -- Rail jumps can occur when dist > 1.5, because the cart can skip over a gap in track -- Grab mob if math.random(1,20) > 15 and not self._passenger then @@ -577,7 +587,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o rou_pos = vector.round(pos) node = minetest.get_node(rou_pos) local g = minetest.get_item_group(node.name, "connect_to_raillike") - if g ~= self._railtype and self._railtype then + if g ~= self._staticdata.railtype and self._staticdata.railtype then -- Detach driver if player then if self._old_pos then @@ -668,7 +678,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end function cart:get_staticdata() - return minetest.serialize({_railtype = self._railtype}) + return minetest.serialize(self._staticdata) end minetest.register_entity(entity_id, cart) @@ -703,7 +713,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) local railtype = minetest.get_item_group(node.name, "connect_to_raillike") local le = cart:get_luaentity() if le then - le._railtype = railtype + le._staticdata.railtype = railtype end local cart_dir if node.name == "mcl_minecarts:golden_rail_on" then From d9d5245d596e2cc1cf8e1d0f22070e3e4460993f Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 13 Feb 2024 14:45:00 +0000 Subject: [PATCH 003/195] Complete rewrite of minecart movement that resolves https://git.minetest.land/MineClone2/MineClone2/issues/2446 and https://git.minetest.land/MineClone2/MineClone2/issues/247#issuecomment-50960 but has a bug where carts will synchronize movements that I am still investigating --- mods/ENTITIES/mcl_minecarts/functions.lua | 2 +- mods/ENTITIES/mcl_minecarts/init.lua | 369 ++++++++++------------ mods/ENTITIES/mcl_minecarts/rails.lua | 25 ++ 3 files changed, 187 insertions(+), 209 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index ba2eff168..52b04d526 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -141,5 +141,5 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) return cur end end - return vector.new(0,0,0) --{x=0, y=0, z=0} + return vector.new(0,0,0) end diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index b1883af72..076be96b3 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -207,9 +207,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o _old_pos = nil, _old_vel = {x=0, y=0, z=0}, _old_switch = 0, - _staticdata = { - railtype = nil, - }, + _staticdata = nil, } function cart:on_activate(staticdata, dtime_s) @@ -221,6 +219,8 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o data.railtype = data._railtype data._railtype = nil end + -- Fix up types + data.dir = vector.new(data.dir) self._staticdata = data end @@ -237,18 +237,28 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) - local pos = self.object:get_pos() - if not self._staticdata.railtype then - local node = minetest.get_node(vector.floor(pos)).name - self._staticdata.railtype = minetest.get_item_group(node, "connect_to_raillike") + local staticdata = self._staticdata + local pos = staticdata.connected_at + if not pos then + print("TODO: handle detached cart behavior") + return end + -- Fix railtype field + if not staticdata.railtype then + local node = minetest.get_node(vector.floor(pos)).name + staticdata.railtype = minetest.get_item_group(node, "connect_to_raillike") + end + + -- Handle punches by something other than the player if not puncher or not puncher:is_player() then - local cart_dir = mcl_minecarts:get_rail_direction(pos, {x=1, y=0, z=0}, nil, nil, self._staticdata.railtype) + local cart_dir = mcl_minecarts:get_rail_direction(pos, {x=1, y=0, z=0}, nil, nil, staticdata.railtype) if vector.equals(cart_dir, {x=0, y=0, z=0}) then return end - mcl_minecarts:set_velocity(self, cart_dir) + + staticdata.dir = cart_dir + self._punched = true return end @@ -288,6 +298,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o return end + -- Handle player punches local vel = self.object:get_velocity() if puncher:get_player_name() == self._driver then if math.abs(vel.x + vel.z) > 7 then @@ -297,206 +308,168 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local punch_dir = mcl_minecarts:velocity_to_dir(puncher:get_look_dir()) punch_dir.y = 0 + local cart_dir = mcl_minecarts:get_rail_direction(pos, punch_dir, nil, nil, self._staticdata.railtype) if vector.equals(cart_dir, {x=0, y=0, z=0}) then return end + staticdata.dir = cart_dir + time_from_last_punch = math.min(time_from_last_punch, tool_capabilities.full_punch_interval) local f = 3 * (time_from_last_punch / tool_capabilities.full_punch_interval) - mcl_minecarts:set_velocity(self, cart_dir, f) + -- Perform acceleration here + staticdata.velocity = (staticdata.velocity or 0 ) + f + local max_vel = mcl_minecarts.speed_max + if staticdata.velocity > max_vel then + staticdata.velocity = max_vel + end end cart.on_activate_by_rail = on_activate_by_rail local passenger_attach_position = vector.new(0, -1.75, 0) - local function do_movement_step( self ) - local vel = self.object:get_velocity() - local pos, rou_pos, node = self.object:get_pos() - local update = {} + local function do_movement_step(self, distance) + local staticdata = self._staticdata + local pos = self.object:get_pos() + local dir = staticdata.dir or vector.new(1,0,0) + dir = vector.new(dir) - local dir, last_switch = nil, nil - if not pos then - pos = self.object:get_pos() + -- Calculate raw location + local next_dir,last_switch = mcl_minecarts:get_rail_direction(pos, dir, nil, nil, staticdata.railtype) + next_dir = vector.new(next_dir) -- Needed to isolate the carts from one another + local next_pos = vector.new(pos + next_dir * distance) + + -- Fix up position + if next_dir.x == 0 then next_pos.x = math.floor(next_pos.x+0.5) end + if next_dir.y == 0 then next_pos.y = math.floor(next_pos.y+0.5) end + if next_dir.z == 0 then next_pos.z = math.floor(next_pos.z+0.5) end + + -- Direction flipped, stop + if next_dir == dir * -1 then + print("Stopping cart") + -- TODO: detach the cart if there isn't a stop after the rail + staticdata.velocity = 0 + next_pos = vector.round(next_pos + dir) end - if self._old_pos and not self._punched then - local flo_pos = vector.floor(pos) - local flo_old = vector.floor(self._old_pos) - if vector.equals(flo_pos, flo_old) and (not has_fuel) then - return - -- Prevent querying the same node over and over again + + -- Update cart orientation + local yaw = 0 + if next_dir.x < 0 then + yaw = 0.5 + elseif next_dir.x > 0 then + yaw = 1.5 + elseif dir.z < 0 then + yaw = 1 + end + self.object:set_yaw( yaw * math.pi ) + + -- Update cart position + local next_pos_rounded = vector.round(next_pos) + staticdata.connected_at = next_pos_rounded + staticdata.dir = next_dir + self.object:move_to(next_pos) + + -- Handle track interactions + local pos_rounded = vector.round(pos) + if pos_rounded ~= next_pos_rounded then + local old_node_name = minetest.get_node(pos_rounded).name + local old_node_def = minetest.registered_nodes[old_node_name] + if old_node_def._mcl_minecarts_on_leave then + old_node_def._mcl_minecarts_on_leave( pos_rounded, self ) end - if not rou_pos then - rou_pos = vector.round(pos) - end - local rou_old = vector.round(self._old_pos) - if not node then - node = minetest.get_node(rou_pos) - end - local node_old = minetest.get_node(rou_old) - - -- Update detector rails - if node.name == "mcl_minecarts:detector_rail" then - local newnode = {name="mcl_minecarts:detector_rail_on", param2 = node.param2} - minetest.swap_node(rou_pos, newnode) - mesecon.receptor_on(rou_pos) - end - if node_old.name == "mcl_minecarts:detector_rail_on" then - local newnode = {name="mcl_minecarts:detector_rail", param2 = node_old.param2} - minetest.swap_node(rou_old, newnode) - mesecon.receptor_off(rou_old) - end - -- Activate minecart if on activator rail - if node_old.name == "mcl_minecarts:activator_rail_on" and self.on_activate_by_rail then - self:on_activate_by_rail() + local new_node_name = minetest.get_node(next_pos_rounded).name + local new_node_def = minetest.registered_nodes[new_node_name] + if new_node_def._mcl_minecarts_on_enter then + new_node_def._mcl_minecarts_on_enter( next_pos_rounded, self ) end end - -- Stop cart if velocity vector flips - if self._old_vel and self._old_vel.y == 0 and - (self._old_vel.x * vel.x < 0 or self._old_vel.z * vel.z < 0) then - self._old_vel = {x = 0, y = 0, z = 0} - self._old_pos = pos - self.object:set_velocity(vector.new()) - self.object:set_acceleration(vector.new()) - return - end - self._old_vel = vector.new(vel) + --print("pos="..tostring(next_pos)..",dir="..tostring(next_dir)..",staticdata="..tostring(staticdata)) + end - if self._old_pos then - local diff = vector.subtract(self._old_pos, pos) - for _,v in ipairs({"x","y","z"}) do - if math.abs(diff[v]) > 1.1 then - local expected_pos = vector.add(self._old_pos, self._old_dir) - dir, last_switch = mcl_minecarts:get_rail_direction(pos, self._old_dir, ctrl, self._old_switch, self._staticdata.railtype) + local function process_acceleration(self, timestep) + local staticdata = self._staticdata - if vector.equals(dir, {x=0, y=0, z=0}) then - dir = false - pos = vector.new(expected_pos) - update.pos = true - end - break - end - end - end + local pos = self.object:get_pos() + local node_name = minetest.get_node(pos).name + local node_def = minetest.registered_nodes[node_name] - if vel.y == 0 then - for _,v in ipairs({"x", "z"}) do - if vel[v] ~= 0 and math.abs(vel[v]) < 0.9 then - vel[v] = 0 - update.vel = true - end - end - end - - local cart_dir = mcl_minecarts:velocity_to_dir(vel) - local max_vel = mcl_minecarts.speed_max - if not dir then - dir, last_switch = mcl_minecarts:get_rail_direction(pos, cart_dir, ctrl, self._old_switch, self._staticdata.railtype) - end - - local new_acc = {x=0, y=0, z=0} - if vector.equals(dir, {x=0, y=0, z=0}) and not has_fuel then - vel = {x=0, y=0, z=0} - update.vel = true + if self._go_forward then + self._acceleration = 2 + elseif self._brake then + self._acceleration = -1.5 + elseif self._punched then + self._acceleration = 2 + elseif self._fueltime and self._fueltime > 0 then + self._acceleration = 0.6 else - -- If the direction changed - if dir.x ~= 0 and self._old_dir.z ~= 0 then - vel.x = dir.x * math.abs(vel.z) - vel.z = 0 - pos.z = math.floor(pos.z + 0.5) - update.pos = true - end - if dir.z ~= 0 and self._old_dir.x ~= 0 then - vel.z = dir.z * math.abs(vel.x) - vel.x = 0 - pos.x = math.floor(pos.x + 0.5) - update.pos = true - end - -- Up, down? - if dir.y ~= self._old_dir.y then - vel.y = dir.y * math.abs(vel.x + vel.z) - pos = vector.round(pos) - update.pos = true - end - - -- Slow down or speed up - local acc = dir.y * -1.8 - local friction = 0.4 - local ndef = minetest.registered_nodes[minetest.get_node(pos).name] - local speed_mod = ndef and ndef._rail_acceleration - - acc = acc - friction - - if has_fuel then - acc = acc + 0.6 - end - - if speed_mod and speed_mod ~= 0 then - acc = acc + speed_mod + friction - end - - new_acc = vector.multiply(dir, acc) + self._acceleration = node_def._rail_acceleration or -0.4 end - self.object:set_acceleration(new_acc) - self._old_pos = vector.new(pos) - self._old_dir = vector.new(dir) - self._old_switch = last_switch + if self._acceleration > 0 and staticdata.velocity < 0.1 then + staticdata.velocity = 0.1 + end - -- Limits - for _,v in ipairs({"x","y","z"}) do - if math.abs(vel[v]) > max_vel then - vel[v] = mcl_minecarts:get_sign(vel[v]) * max_vel - new_acc[v] = 0 - update.vel = true + if math.abs(self._acceleration) > 0.1 then + staticdata.velocity = ( staticdata.velocity or 0 ) + self._acceleration * timestep + local max_vel = mcl_minecarts.speed_max + if staticdata.velocity > max_vel then + staticdata.velocity = max_vel + elseif staticdata.velocity < 0.1 then + staticdata.velocity = 0 end end + if false and staticdata.velocity > 0 then + print( "acceleration="..tostring(self._acceleration)..",velocity="..tostring(staticdata.velocity).. + ",timestep="..tostring(timestep)) + end + end - if update.pos or self._punched then - local yaw = 0 - if dir.x < 0 then - yaw = 0.5 - elseif dir.x > 0 then - yaw = 1.5 - elseif dir.z < 0 then - yaw = 1 + + local function do_movement( self, dtime ) + local staticdata = self._staticdata + + local total_distance = dtime * ( staticdata.velocity or 0 ) + local remaining_distance = total_distance + local max_step_distance = 0.5 + local steps = math.ceil(total_distance / max_step_distance ) + + process_acceleration(self,dtime) + + for i = 1,steps do + local step_distance = max_step_distance + if remaining_distance < max_step_distance then + step_distance = remaining_distance + remaining_distance = 0 + else + remaining_distance = remaining_distance - max_step_distance end - self.object:set_yaw(yaw * math.pi) + + if i ~= 1 then + -- Go faster/slower + local timestep = dtime * step_distance / total_distance + process_acceleration(self, timestep) + end + + do_movement_step(self,step_distance) end - if self._punched then - self._punched = false - end - - if not (update.vel or update.pos) then - return - end - - local anim = {x=0, y=0} - if dir.y == -1 then - anim = {x=1, y=1} - elseif dir.y == 1 then - anim = {x=2, y=2} - end - self.object:set_animation(anim, 1, 0) - - self.object:set_velocity(vel) - if update.pos then - self.object:set_pos(pos) - end + -- Clear punched flag now that movement for this step has been completed + self._punched = false end function cart:on_step(dtime) hopper_take_item(self, dtime) + local staticdata = self._staticdata - local vel = self.object:get_velocity() local pos, rou_pos, node = self.object:get_pos() local update = {} + local acceleration = 0 -- Controls local ctrl, player = nil, nil @@ -509,6 +482,10 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o detach_driver(self) return end + + -- Experimental controls + self._go_forward = ctrl.up + self._brake = ctrl.down end -- Give achievement when player reached a distance of 1000 nodes from the start position @@ -517,15 +494,9 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end end - -- Go faster when punched - if self._punched then - vel = vector.add(vel, self._velocity) - self.object:set_velocity(vel) - self._old_dir.y = 0 - elseif vector.equals(vel, {x=0, y=0, z=0}) and (not has_fuel) then - return - end + do_movement(self, dtime) + -- TODO: move this into do_movement_step -- Drop minecart if it collides with a cactus node local r = 0.6 for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do @@ -539,20 +510,6 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end end - -- Debug - if false then - local node = minetest.get_node(pos).name - local dist = 0 - if pos and self._old_pos then - dist = vector.distance(pos,self._old_pos) - end - if dist > 1.5 then - print("pos="..tostring(pos)..",dist="..tostring(dist)..",node="..tostring(node)..",old_pos=".. - tostring(self._old_pos)..",vel="..tostring(vel)) - end - -- Rail jumps can occur when dist > 1.5, because the cart can skip over a gap in track - end - -- Grab mob if math.random(1,20) > 15 and not self._passenger then if self.name == "mcl_minecarts:minecart" then @@ -673,8 +630,6 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o self._blinktimer = tnt.BLINKTIMER end end - - do_movement_step(self) end function cart:get_staticdata() @@ -701,30 +656,28 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) return end - -- Activate detector rail - if node.name == "mcl_minecarts:detector_rail" then - local newnode = {name="mcl_minecarts:detector_rail_on", param2 = node.param2} - minetest.swap_node(railpos, newnode) - mesecon.receptor_on(railpos) - end - local entity_id = entity_mapping[itemstack:get_name()] local cart = minetest.add_entity(railpos, entity_id) local railtype = minetest.get_item_group(node.name, "connect_to_raillike") + local cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype) + cart:set_yaw(minetest.dir_to_yaw(cart_dir)) + + -- Update static data local le = cart:get_luaentity() if le then - le._staticdata.railtype = railtype + le._staticdata = { + railtype = railtype, + connected_at = railpos, + dir = vector.new(cart_dir), + velocity = 0, + } end - local cart_dir - if node.name == "mcl_minecarts:golden_rail_on" then - cart_dir = mcl_minecarts:get_start_direction(railpos) + + -- Handle track behaviors + local node_def = minetest.registered_nodes[node.name] + if node_def._mcl_minecarts_on_enter then + node_def._mcl_minecarts_on_enter(railpos, cart) end - if cart_dir then - mcl_minecarts:set_velocity(le, cart_dir) - else - cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype) - end - cart:set_yaw(minetest.dir_to_yaw(cart_dir)) local pname = "" if placer then diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 17c854e68..a5b967a07 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -181,6 +181,11 @@ register_rail("mcl_minecarts:activator_rail_on", }, }, + _mcl_minecarts_on_enter = function(pos, cart) + if cart.on_activate_by_rail then + cart:on_activate_by_rail() + end + end, drop = "mcl_minecarts:activator_rail", }, false @@ -200,6 +205,16 @@ register_rail("mcl_minecarts:detector_rail", rules = rail_rules_short, }, }, + _mcl_minecarts_on_enter = function(pos, cart) + local node = minetest.get_node(pos) + + local newnode = { + name = "mcl_minecarts:detector_rail_on", + param2 = node.param2 + } + minetest.swap_node( pos, newnode ) + mesecon.receptor_on(pos) + end, } ) @@ -214,6 +229,16 @@ register_rail("mcl_minecarts:detector_rail_on", rules = rail_rules_short, }, }, + _mcl_minecarts_on_leave = function(pos, cart) + local node = minetest.get_node(pos) + + local newnode = { + name = "mcl_minecarts:detector_rail", + param2 = node.param2 + } + minetest.swap_node( pos, newnode ) + mesecon.receptor_off(pos) + end, drop = "mcl_minecarts:detector_rail", }, false From c489e0c380a0eff6708f6309d00111f1728e5e72 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 13 Feb 2024 14:57:45 +0000 Subject: [PATCH 004/195] Remove now unused properties from minecart definition, convert more vectors to use vector.new syntax --- mods/ENTITIES/mcl_minecarts/init.lua | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 076be96b3..1b607adf1 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -29,7 +29,7 @@ local function detach_driver(self) self._start_pos = nil if player then player:set_detach() - player:set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0}) + player:set_eye_offset(vector.new(0,0,0),vector.new(0,0,0)) mcl_player.player_set_animation(player, "stand" , 30) end end @@ -196,17 +196,13 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o _driver = nil, -- player who sits in and controls the minecart (only for minecart!) _passenger = nil, -- for mobs _punched = false, -- used to re-send _velocity and position - _velocity = {x=0, y=0, z=0}, -- only used on punch _start_pos = nil, -- Used to calculate distance for “On A Rail” achievement _last_float_check = nil, -- timestamp of last time the cart was checked to be still on a rail _fueltime = nil, -- how many seconds worth of fuel is left. Only used by minecart with furnace _boomtimer = nil, -- how many seconds are left before exploding _blinktimer = nil, -- how many seconds are left before TNT blinking _blink = false, -- is TNT blink texture active? - _old_dir = {x=0, y=0, z=0}, _old_pos = nil, - _old_vel = {x=0, y=0, z=0}, - _old_switch = 0, _staticdata = nil, } @@ -252,8 +248,8 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o -- Handle punches by something other than the player if not puncher or not puncher:is_player() then - local cart_dir = mcl_minecarts:get_rail_direction(pos, {x=1, y=0, z=0}, nil, nil, staticdata.railtype) - if vector.equals(cart_dir, {x=0, y=0, z=0}) then + local cart_dir = mcl_minecarts:get_rail_direction(pos, vector.new(1,0,0), nil, nil, staticdata.railtype) + if vector.equals(cart_dir, vector.new(0,0,0)) then return end @@ -310,7 +306,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o punch_dir.y = 0 local cart_dir = mcl_minecarts:get_rail_direction(pos, punch_dir, nil, nil, self._staticdata.railtype) - if vector.equals(cart_dir, {x=0, y=0, z=0}) then + if vector.equals(cart_dir, vector.new(0,0,0)) then return end @@ -552,7 +548,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end mcl_player.player_attached[self._driver] = nil player:set_detach() - player:set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0}) + player:set_eye_offset(vector.new(0,0,0),vector.new(0,0,0)) end -- Explode if already ignited @@ -659,7 +655,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) local entity_id = entity_mapping[itemstack:get_name()] local cart = minetest.add_entity(railpos, entity_id) local railtype = minetest.get_item_group(node.name, "connect_to_raillike") - local cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype) + local cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0), nil, nil, railtype) cart:set_yaw(minetest.dir_to_yaw(cart_dir)) -- Update static data @@ -719,7 +715,7 @@ local function register_craftitem(itemstring, entity_id, description, tt_help, l local placed if minetest.get_item_group(dropnode.name, "rail") ~= 0 then -- FIXME: This places minecarts even if the spot is already occupied - local pointed_thing = { under = droppos, above = { x=droppos.x, y=droppos.y+1, z=droppos.z } } + local pointed_thing = { under = droppos, above = vector.new( droppos.x, droppos.y+1, droppos.z ) } placed = mcl_minecarts.place_minecart(stack, pointed_thing) end if placed == nil then @@ -788,13 +784,13 @@ register_minecart( self._driver = player_name self._start_pos = self.object:get_pos() mcl_player.player_attached[player_name] = true - clicker:set_attach(self.object, "", {x=0, y=-1.75, z=-2}, {x=0, y=0, z=0}) + clicker:set_attach(self.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) mcl_player.player_attached[name] = true minetest.after(0.2, function(name) local player = minetest.get_player_by_name(name) if player then mcl_player.player_set_animation(player, "sit" , 30) - player:set_eye_offset({x=0, y=-5.5, z=0},{x=0, y=-4, z=0}) + player:set_eye_offset(vector.new(0,-5.5,0), vector.new(0,-4,0)) mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) end end, name) From 0c46c2fcc2aea5cdb74253d47a82a866aa452c41 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 13 Feb 2024 15:28:00 +0000 Subject: [PATCH 005/195] Change left,right and back vectors to matrix math results with no branching --- mods/ENTITIES/mcl_minecarts/functions.lua | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 52b04d526..587bbd281 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -76,16 +76,10 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) local cur local left_check, right_check = true, true - -- Check left and right - local left = vector.new(0, 0, 0) - local right = vector.new(0, 0, 0) - if dir.z ~= 0 and dir.x == 0 then - left.x = -dir.z - right.x = dir.z - elseif dir.x ~= 0 and dir.z == 0 then - left.z = dir.x - right.z = -dir.x - end + -- Calculate left, right and back + local left = vector.new(-dir.z, dir.y, dir.x) + local right = vector.new( dir.z, dir.y, -dir.x) + local back = vector.new(-dir.x, dir.y, -dir.z) if ctrl then if old_switch == 1 then @@ -132,11 +126,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) end -- Backwards if not old_switch then - cur = mcl_minecarts:check_front_up_down(pos, vector.new( - -dir.x, - dir.y, - -dir.z - ), true, railtype) + cur = mcl_minecarts:check_front_up_down(pos, back, true, railtype) if cur then return cur end From 70aa9004be5806b0ff9b7ea94cf07085ec3a9666 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 13 Feb 2024 15:33:37 +0000 Subject: [PATCH 006/195] Fix initial_properties for minecarts --- mods/ENTITIES/mcl_minecarts/init.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 1b607adf1..a52e687c2 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -184,12 +184,14 @@ local entity_mapping = {} local function register_entity(entity_id, mesh, textures, drop, on_rightclick, on_activate_by_rail) local cart = { - physical = false, - collisionbox = {-10/16., -0.5, -10/16, 10/16, 0.25, 10/16}, - visual = "mesh", - mesh = mesh, - visual_size = {x=1, y=1}, - textures = textures, + initial_properties = { + physical = false, + collisionbox = {-10/16., -0.5, -10/16, 10/16, 0.25, 10/16}, + visual = "mesh", + mesh = mesh, + visual_size = {x=1, y=1}, + textures = textures, + }, on_rightclick = on_rightclick, From 960192024f718cc819289d259b0c2b71e1e8f6fd Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 13 Feb 2024 23:31:48 +0000 Subject: [PATCH 007/195] Add DEBUG flag, stop small do_movement_step's from occuring (this improves but doesn't eliminate the bug I with the a6be179ed commit), add recovery when staticdata field gets lost --- mods/ENTITIES/mcl_minecarts/init.lua | 62 +++++++++++++++++++++------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index a52e687c2..d3f2275ac 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -12,6 +12,7 @@ dofile(mcl_minecarts.modpath.."/functions.lua") dofile(mcl_minecarts.modpath.."/rails.lua") local LOGGING_ON = minetest.settings:get_bool("mcl_logging_minecarts", false) +local DEBUG = false local function mcl_log(message) if LOGGING_ON then mcl_util.mcl_log(message, "[Minecarts]", true) @@ -182,6 +183,15 @@ end -- Table for item-to-entity mapping. Keys: itemstring, Values: Corresponding entity ID local entity_mapping = {} +local function make_staticdata( railtype, connected_at, dir ) + return { + railtype = railtype, + connected_at = connected_at, + dir = vector.new(dir), + velocity = 0 + } +end + local function register_entity(entity_id, mesh, textures, drop, on_rightclick, on_activate_by_rail) local cart = { initial_properties = { @@ -236,10 +246,16 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) local staticdata = self._staticdata + if not staticdata then + staticdata = make_staticdata() + self._staticdata = staticdata + end + local pos = staticdata.connected_at if not pos then + pos = self.object:get_pos() print("TODO: handle detached cart behavior") - return + --return end -- Fix railtype field @@ -370,6 +386,21 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o staticdata.dir = next_dir self.object:move_to(next_pos) + if DEBUG and self._driver then + local prefix = " " + if next_dir ~= dir then + prefix = "--->" + end + print( prefix + .. "pos="..tostring(pos) + ..",dir="..tostring(dir) + ..",next_dir="..tostring(next_dir) + ..",next_pos="..tostring(next_pos) + ..",velocity="..tostring(staticdata.velocity) + ..",distance="..tostring(distance) + ) + end + -- Handle track interactions local pos_rounded = vector.round(pos) if pos_rounded ~= next_pos_rounded then @@ -385,8 +416,6 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o new_node_def._mcl_minecarts_on_enter( next_pos_rounded, self ) end end - - --print("pos="..tostring(next_pos)..",dir="..tostring(next_dir)..",staticdata="..tostring(staticdata)) end local function process_acceleration(self, timestep) @@ -432,12 +461,15 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local function do_movement( self, dtime ) local staticdata = self._staticdata + -- Break long movements into fixed-size steps so that + -- it is impossible to jump across gaps due to server lag + -- causing large timesteps local total_distance = dtime * ( staticdata.velocity or 0 ) local remaining_distance = total_distance local max_step_distance = 0.5 local steps = math.ceil(total_distance / max_step_distance ) - process_acceleration(self,dtime) + process_acceleration(self,dtime * max_step_distance / total_distance) for i = 1,steps do local step_distance = max_step_distance @@ -448,13 +480,16 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o remaining_distance = remaining_distance - max_step_distance end + -- Don't try to perform very small steps + if step_distance < 0.001 then break end + + -- Handle acceleration on everything except the first step, as that was + -- done outside the loop if i ~= 1 then - -- Go faster/slower - local timestep = dtime * step_distance / total_distance - process_acceleration(self, timestep) + process_acceleration(self, dtime * step_distance / total_distance) end - do_movement_step(self,step_distance) + do_movement_step(self, step_distance) end -- Clear punched flag now that movement for this step has been completed @@ -464,6 +499,10 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o function cart:on_step(dtime) hopper_take_item(self, dtime) local staticdata = self._staticdata + if not staticdata then + staticdata = make_staticdata() + self._staticdata = staticdata + end local pos, rou_pos, node = self.object:get_pos() local update = {} @@ -663,12 +702,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) -- Update static data local le = cart:get_luaentity() if le then - le._staticdata = { - railtype = railtype, - connected_at = railpos, - dir = vector.new(cart_dir), - velocity = 0, - } + le._staticdata = make_staticdata( railtype, railpos, cart_dir ) end -- Handle track behaviors From 05aef666684987719fb3783714b11b0c84887adc Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 14 Feb 2024 10:48:55 +0000 Subject: [PATCH 008/195] Modify do_movement_step to move to always move to the edge of the current rail segment before updating the direction to prevent oscillations at corners, fix end of track stop location with new movement behavior, disable experimental controls, swap code to detach the driver on float with a call to detach_driver() --- mods/ENTITIES/mcl_minecarts/init.lua | 113 ++++++++++++++++++--------- 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index d3f2275ac..0d03dde86 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -7,6 +7,7 @@ mcl_minecarts = {} mcl_minecarts.modpath = minetest.get_modpath(modname) mcl_minecarts.speed_max = 10 mcl_minecarts.check_float_time = 15 +local max_step_distance = 0.5 dofile(mcl_minecarts.modpath.."/functions.lua") dofile(mcl_minecarts.modpath.."/rails.lua") @@ -192,6 +193,22 @@ local function make_staticdata( railtype, connected_at, dir ) } end +local function to_dirstring(dir) + if dir.x == 0 then + if dir.z == 1 then + return "north" + else + return "south" + end + elseif dir.z == 0 then + if dir.x == 1 then + return " east" + else + return " west" + end + end +end + local function register_entity(entity_id, mesh, textures, drop, on_rightclick, on_activate_by_rail) local cart = { initial_properties = { @@ -345,16 +362,51 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local passenger_attach_position = vector.new(0, -1.75, 0) - local function do_movement_step(self, distance) + local function distance_to_next_block( dir, pos ) + if dir.x == 1 then + return 1 - ( pos.x - math.floor(pos.x) ) + elseif dir.x == -1 then + return pos.x - math.floor(pos.x) + elseif dir.z == 1 then + return 1 - ( pos.z - math.floor(pos.z) ) + elseif dir.z == -1 then + return pos.z - math.floor(pos.z) + elseif dir.y == 1 then + return 1 - ( pos.y - math.floor(pos.z) ) + else + return pos.y - math.floor(pos.y) + end + end + + local function do_movement_step(self, remaining_distance) local staticdata = self._staticdata local pos = self.object:get_pos() local dir = staticdata.dir or vector.new(1,0,0) dir = vector.new(dir) - -- Calculate raw location - local next_dir,last_switch = mcl_minecarts:get_rail_direction(pos, dir, nil, nil, staticdata.railtype) - next_dir = vector.new(next_dir) -- Needed to isolate the carts from one another - local next_pos = vector.new(pos + next_dir * distance) + -- Calculate the distance to the next block + -- This is just short of a full block to keep from jumping + local distance_to_next = distance_to_next_block( dir, pos ) - 0.01 + local next_pos + local next_dir,last_switch + next_dir = dir + if distance_to_next < 0.01 then + distance_to_next = 0.5 + + -- Calculate next direction + next_dir,last_switch = mcl_minecarts:get_rail_direction(pos, dir, nil, nil, staticdata.railtype) + next_dir = vector.copy(next_dir) -- Needed to isolate the carts from one another + elseif distance_to_next > max_step_distance then + distance_to_next = max_step_distance + end + + local distance = remaining_distance + if distance > distance_to_next then + distance = distance_to_next + end + + -- Calculate next position + next_pos = vector.new(pos + next_dir * distance) -- Fix up position if next_dir.x == 0 then next_pos.x = math.floor(next_pos.x+0.5) end @@ -363,10 +415,14 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o -- Direction flipped, stop if next_dir == dir * -1 then - print("Stopping cart") -- TODO: detach the cart if there isn't a stop after the rail staticdata.velocity = 0 - next_pos = vector.round(next_pos + dir) + next_pos = vector.round(next_pos) + + if DEBUG and self._driver then + local node_name = minetest.get_node(next_pos).name + print("Stopping cart on "..node_name.." at "..tostring(next_pos)) + end end -- Update cart orientation @@ -393,8 +449,8 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end print( prefix .. "pos="..tostring(pos) - ..",dir="..tostring(dir) - ..",next_dir="..tostring(next_dir) + ..",dir="..to_dirstring(dir) + ..",next_dir="..to_dirstring(next_dir) ..",next_pos="..tostring(next_pos) ..",velocity="..tostring(staticdata.velocity) ..",distance="..tostring(distance) @@ -416,6 +472,9 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o new_node_def._mcl_minecarts_on_enter( next_pos_rounded, self ) end end + + -- Report distance traveled + return distance end local function process_acceleration(self, timestep) @@ -466,30 +525,15 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o -- causing large timesteps local total_distance = dtime * ( staticdata.velocity or 0 ) local remaining_distance = total_distance - local max_step_distance = 0.5 - local steps = math.ceil(total_distance / max_step_distance ) process_acceleration(self,dtime * max_step_distance / total_distance) - for i = 1,steps do - local step_distance = max_step_distance - if remaining_distance < max_step_distance then - step_distance = remaining_distance - remaining_distance = 0 - else - remaining_distance = remaining_distance - max_step_distance - end - - -- Don't try to perform very small steps - if step_distance < 0.001 then break end - - -- Handle acceleration on everything except the first step, as that was - -- done outside the loop - if i ~= 1 then + while remaining_distance > 0.1 do + local step_distance = do_movement_step(self, remaining_distance) + if step_distance > 0.1 then process_acceleration(self, dtime * step_distance / total_distance) end - - do_movement_step(self, step_distance) + remaining_distance = remaining_distance - step_distance end -- Clear punched flag now that movement for this step has been completed @@ -521,8 +565,8 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end -- Experimental controls - self._go_forward = ctrl.up - self._brake = ctrl.down + --self._go_forward = ctrl.up + --self._brake = ctrl.down end -- Give achievement when player reached a distance of 1000 nodes from the start position @@ -583,14 +627,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local g = minetest.get_item_group(node.name, "connect_to_raillike") if g ~= self._staticdata.railtype and self._staticdata.railtype then -- Detach driver - if player then - if self._old_pos then - self.object:set_pos(self._old_pos) - end - mcl_player.player_attached[self._driver] = nil - player:set_detach() - player:set_eye_offset(vector.new(0,0,0),vector.new(0,0,0)) - end + detach_driver(self) -- Explode if already ignited if self._boomtimer then From f082e4a554363e538dc70553bb233b53c4c1e067 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 14 Feb 2024 11:47:42 +0000 Subject: [PATCH 009/195] Make minecart always stop at correct location at end of track, fix crash when placing chest minecart after changing how staticdata is handled --- mods/ENTITIES/mcl_minecarts/init.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 0d03dde86..2b0561636 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -417,11 +417,17 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o if next_dir == dir * -1 then -- TODO: detach the cart if there isn't a stop after the rail staticdata.velocity = 0 - next_pos = vector.round(next_pos) + local next_pos_before_round = vector.copy(next_pos) + next_pos = vector.round(next_pos + dir * 0.5) if DEBUG and self._driver then local node_name = minetest.get_node(next_pos).name - print("Stopping cart on "..node_name.." at "..tostring(next_pos)) + print("Stopping cart on "..node_name.." at "..tostring(next_pos) + .." pos="..tostring(pos) + ..",next_pos="..tostring(next_pos) + ..",next_pos_before_round="..tostring(next_pos_before_round) + ..",distance="..distance + ) end end @@ -707,7 +713,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end function cart:get_staticdata() - return minetest.serialize(self._staticdata) + return minetest.serialize(self._staticdata or {}) end minetest.register_entity(entity_id, cart) From 2e78e9cdade1d99af1b39687e632ab6cf09bf2db Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 16 Feb 2024 23:09:29 +0000 Subject: [PATCH 010/195] Change connected railcar behavior to fix unreliable end of track stopping, set maximum acceleration of powered rails to 8 blocks per second (per https://minecraft.fandom.com/wiki/Powered_Rail), stop powered rails from powering the block underneath it (allows below rail hopper to work while the rail is powered like in https://www.youtube.com/watch?v=szjO0-duTAk), modify mcl_hoppers to allow triggering a hopper pull once the minecart is stopped on top of the hopper and wait before allowing the cart to move to allow redstone circuits time to process --- mods/ENTITIES/mcl_minecarts/init.lua | 173 ++++++++++++-------------- mods/ENTITIES/mcl_minecarts/rails.lua | 3 +- mods/ITEMS/mcl_hoppers/init.lua | 7 +- 3 files changed, 88 insertions(+), 95 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 2b0561636..1a6115c81 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -188,8 +188,9 @@ local function make_staticdata( railtype, connected_at, dir ) return { railtype = railtype, connected_at = connected_at, + distance = 0, + velocity = 0, dir = vector.new(dir), - velocity = 0 } end @@ -380,115 +381,92 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local function do_movement_step(self, remaining_distance) local staticdata = self._staticdata - local pos = self.object:get_pos() - local dir = staticdata.dir or vector.new(1,0,0) - dir = vector.new(dir) - -- Calculate the distance to the next block - -- This is just short of a full block to keep from jumping - local distance_to_next = distance_to_next_block( dir, pos ) - 0.01 - local next_pos - local next_dir,last_switch - next_dir = dir - if distance_to_next < 0.01 then - distance_to_next = 0.5 + local pos = staticdata.connected_at - -- Calculate next direction - next_dir,last_switch = mcl_minecarts:get_rail_direction(pos, dir, nil, nil, staticdata.railtype) - next_dir = vector.copy(next_dir) -- Needed to isolate the carts from one another - elseif distance_to_next > max_step_distance then - distance_to_next = max_step_distance - end + if not pos then return remaining_distance end + if staticdata.velocity < 0.1 then return remaining_distance end - local distance = remaining_distance - if distance > distance_to_next then - distance = distance_to_next - end + local remaining_in_block = 1 - ( staticdata.distance or 0 ) + local dinstance = 0 + if remaining_in_block > remaining_distance then + distance = remaining_distance + staticdata.distance = ( staticdata.distance or 0 ) + distance + pos = pos + staticdata.dir * staticdata.distance + else + distance = remaining_in_block + staticdata.distance = 0 - -- Calculate next position - next_pos = vector.new(pos + next_dir * distance) - - -- Fix up position - if next_dir.x == 0 then next_pos.x = math.floor(next_pos.x+0.5) end - if next_dir.y == 0 then next_pos.y = math.floor(next_pos.y+0.5) end - if next_dir.z == 0 then next_pos.z = math.floor(next_pos.z+0.5) end - - -- Direction flipped, stop - if next_dir == dir * -1 then - -- TODO: detach the cart if there isn't a stop after the rail - staticdata.velocity = 0 - local next_pos_before_round = vector.copy(next_pos) - next_pos = vector.round(next_pos + dir * 0.5) - - if DEBUG and self._driver then - local node_name = minetest.get_node(next_pos).name - print("Stopping cart on "..node_name.." at "..tostring(next_pos) - .." pos="..tostring(pos) - ..",next_pos="..tostring(next_pos) - ..",next_pos_before_round="..tostring(next_pos_before_round) - ..",distance="..distance - ) + -- Leave the old node + local old_node_name = minetest.get_node(pos).name + local old_node_def = minetest.registered_nodes[old_node_name] + if old_node_def._mcl_minecarts_on_leave then + old_node_def._mcl_minecarts_on_leave( pos, self ) end + + -- Anchor at the next node + pos = pos + staticdata.dir + staticdata.connected_at = pos + + -- Enter the new node + local new_node_name = minetest.get_node(pos).name + local new_node_def = minetest.registered_nodes[new_node_name] + if new_node_def._mcl_minecarts_on_enter then + new_node_def._mcl_minecarts_on_enter( pos, self ) + end + + -- check for hopper under the rail + local under_pos = pos - vector.new(0,1,0) + local under_node_name = minetest.get_node(under_pos).name + local under_node_def = minetest.registered_nodes[under_node_name] + print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) + if under_node_def and under_node_def.groups.hopper ~= 0 then + print( "Attempt pull_from_minecart" ) + if mcl_hoppers.pull_from_minecart( self, under_pos, self._inv_size or 0 ) then + staticdata.delay = 1.5 + end + end + + -- Get the next direction + next_dir,last_switch = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) + + -- Handle end of track + if next_dir == staticdata.dir * -1 then + print("Stopping cart at "..tostring(pos)) + staticdata.velocity = 0 + distence = remaining_distance + end + + -- Update cart direction + staticdata.dir = next_dir end + self.object:move_to(pos) + -- Update cart orientation local yaw = 0 - if next_dir.x < 0 then + local dir = staticdata.dir + if dir.x < 0 then yaw = 0.5 - elseif next_dir.x > 0 then + elseif dir.x > 0 then yaw = 1.5 elseif dir.z < 0 then yaw = 1 end self.object:set_yaw( yaw * math.pi ) - -- Update cart position - local next_pos_rounded = vector.round(next_pos) - staticdata.connected_at = next_pos_rounded - staticdata.dir = next_dir - self.object:move_to(next_pos) - - if DEBUG and self._driver then - local prefix = " " - if next_dir ~= dir then - prefix = "--->" - end - print( prefix - .. "pos="..tostring(pos) - ..",dir="..to_dirstring(dir) - ..",next_dir="..to_dirstring(next_dir) - ..",next_pos="..tostring(next_pos) - ..",velocity="..tostring(staticdata.velocity) - ..",distance="..tostring(distance) - ) - end - - -- Handle track interactions - local pos_rounded = vector.round(pos) - if pos_rounded ~= next_pos_rounded then - local old_node_name = minetest.get_node(pos_rounded).name - local old_node_def = minetest.registered_nodes[old_node_name] - if old_node_def._mcl_minecarts_on_leave then - old_node_def._mcl_minecarts_on_leave( pos_rounded, self ) - end - - local new_node_name = minetest.get_node(next_pos_rounded).name - local new_node_def = minetest.registered_nodes[new_node_name] - if new_node_def._mcl_minecarts_on_enter then - new_node_def._mcl_minecarts_on_enter( next_pos_rounded, self ) - end - end - -- Report distance traveled return distance end local function process_acceleration(self, timestep) local staticdata = self._staticdata + if not staticdata.connected_at then return end - local pos = self.object:get_pos() + local pos = staticdata.connected_at local node_name = minetest.get_node(pos).name local node_def = minetest.registered_nodes[node_name] + local max_vel = mcl_minecarts.speed_max if self._go_forward then self._acceleration = 2 @@ -499,16 +477,19 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o elseif self._fueltime and self._fueltime > 0 then self._acceleration = 0.6 else - self._acceleration = node_def._rail_acceleration or -0.4 + if staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then + self._acceleration = 0 + else + self._acceleration = node_def._rail_acceleration or -0.4 + end end - if self._acceleration > 0 and staticdata.velocity < 0.1 then + if self._acceleration > 0 and (staticdata.velocity or 0) < 0.1 then staticdata.velocity = 0.1 end if math.abs(self._acceleration) > 0.1 then staticdata.velocity = ( staticdata.velocity or 0 ) + self._acceleration * timestep - local max_vel = mcl_minecarts.speed_max if staticdata.velocity > max_vel then staticdata.velocity = max_vel elseif staticdata.velocity < 0.1 then @@ -516,8 +497,8 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end end - if false and staticdata.velocity > 0 then - print( "acceleration="..tostring(self._acceleration)..",velocity="..tostring(staticdata.velocity).. + if DEBUG and staticdata.velocity > 0 then + print( " acceleration="..tostring(self._acceleration)..",velocity="..tostring(staticdata.velocity).. ",timestep="..tostring(timestep)) end end @@ -526,6 +507,14 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local function do_movement( self, dtime ) local staticdata = self._staticdata + -- Allow the carts to be delay for the rest of the world to react before moving again + if ( staticdata.delay or 0 ) > dtime then + staticdata.delay = staticdata.delay - dtime + return + else + staticdata.delay = 0 + end + -- Break long movements into fixed-size steps so that -- it is impossible to jump across gaps due to server lag -- causing large timesteps @@ -555,8 +544,8 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end local pos, rou_pos, node = self.object:get_pos() - local update = {} - local acceleration = 0 + --local update = {} + --local acceleration = 0 -- Controls local ctrl, player = nil, nil diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index a5b967a07..802d747e4 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -50,7 +50,7 @@ end local rail_rules_long = {{x=-1, y= 0, z= 0, spread=true}, {x= 1, y= 0, z= 0, spread=true}, - {x= 0, y=-1, z= 0, spread=true}, +-- {x= 0, y=-1, z= 0, spread=true}, {x= 0, y= 1, z= 0, spread=true}, {x= 0, y= 0, z=-1, spread=true}, {x= 0, y= 0, z= 1, spread=true}, @@ -105,6 +105,7 @@ register_rail("mcl_minecarts:golden_rail_on", { _doc_items_create_entry = false, _rail_acceleration = 4, + _max_acceleration_velocity = 8, mesecons = { conductor = { state = mesecon.state.on, diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index ec7f7ab8f..9af8ff650 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -9,6 +9,8 @@ local function mcl_log(message) end end +mcl_hoppers = {} + --[[ BEGIN OF NODE DEFINITIONS ]] local mcl_hoppers_formspec = table.concat({ @@ -464,8 +466,8 @@ local function hopper_pull_from_mc(mc_ent, dest_pos, inv_size) dest_inv:add_item("main", stack:take_item()) inv:set_stack("main", i, stack) - -- Take one item and stop until next time - return + -- Take one item and stop until next time, report that we took something + return true else mcl_log("no Room") end @@ -475,6 +477,7 @@ local function hopper_pull_from_mc(mc_ent, dest_pos, inv_size) end end end +mcl_hoppers.pull_from_minecart = hopper_pull_from_mc local function hopper_push_to_mc(mc_ent, dest_pos, inv_size) local dest_inv = mcl_entity_invs.load_inv(mc_ent, inv_size) From 8709ba7be82de6b266b4866aad8004a6dc982e7a Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 6 Mar 2024 08:07:31 +0000 Subject: [PATCH 011/195] Implement gravity, move orientation update to own function, fix cart stopping in process_acceleration --- mods/ENTITIES/mcl_minecarts/init.lua | 138 ++++++++++++++++++--------- 1 file changed, 93 insertions(+), 45 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 1a6115c81..559ada225 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -363,20 +363,28 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local passenger_attach_position = vector.new(0, -1.75, 0) - local function distance_to_next_block( dir, pos ) - if dir.x == 1 then - return 1 - ( pos.x - math.floor(pos.x) ) - elseif dir.x == -1 then - return pos.x - math.floor(pos.x) - elseif dir.z == 1 then - return 1 - ( pos.z - math.floor(pos.z) ) - elseif dir.z == -1 then - return pos.z - math.floor(pos.z) - elseif dir.y == 1 then - return 1 - ( pos.y - math.floor(pos.z) ) + local function update_cart_orientation(self,staticdata) + local rot = self.object:get_rotation() + local dir = staticdata.dir + if dir.x < 0 then + rot.y = 0.5 * math.pi + elseif dir.x > 0 then + rot.y = 1.5 * math.pi + elseif dir.z < 0 then + rot.y = 1 * math.pi else - return pos.y - math.floor(pos.y) + rot.y = 0 end + + -- Forward/backwards tilt (pitch) + if dir.y < 0 then + rot.x = -0.25 * math.pi + elseif dir.y > 0 then + rot.x = 0.25 * math.pi + else + rot.x = 0 + end + self.object:set_rotation(rot) end local function do_movement_step(self, remaining_distance) @@ -419,20 +427,23 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local under_pos = pos - vector.new(0,1,0) local under_node_name = minetest.get_node(under_pos).name local under_node_def = minetest.registered_nodes[under_node_name] - print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) + if DEBUG then print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) end if under_node_def and under_node_def.groups.hopper ~= 0 then - print( "Attempt pull_from_minecart" ) + if DEBUG then print( "Attempt pull_from_minecart" ) end if mcl_hoppers.pull_from_minecart( self, under_pos, self._inv_size or 0 ) then staticdata.delay = 1.5 end end -- Get the next direction - next_dir,last_switch = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) + local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) + if DEBUG and next_dir ~= staticdata.dir then + print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) + end -- Handle end of track if next_dir == staticdata.dir * -1 then - print("Stopping cart at "..tostring(pos)) + if DEBUG then print("Stopping cart at "..tostring(pos)) end staticdata.velocity = 0 distence = remaining_distance end @@ -444,16 +455,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o self.object:move_to(pos) -- Update cart orientation - local yaw = 0 - local dir = staticdata.dir - if dir.x < 0 then - yaw = 0.5 - elseif dir.x > 0 then - yaw = 1.5 - elseif dir.z < 0 then - yaw = 1 - end - self.object:set_yaw( yaw * math.pi ) + update_cart_orientation(self,staticdata) -- Report distance traveled return distance @@ -463,33 +465,39 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local staticdata = self._staticdata if not staticdata.connected_at then return end + local acceleration = 0 + local friction = 0.1 + + if DEBUG and staticdata.velocity > 0 then + print( " acceleration="..tostring(acceleration)..",velocity="..tostring(staticdata.velocity).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir)) + end + local pos = staticdata.connected_at local node_name = minetest.get_node(pos).name local node_def = minetest.registered_nodes[node_name] local max_vel = mcl_minecarts.speed_max if self._go_forward then - self._acceleration = 2 + acceleration = 2 elseif self._brake then - self._acceleration = -1.5 + acceleration = -1.5 elseif self._punched then - self._acceleration = 2 - elseif self._fueltime and self._fueltime > 0 then - self._acceleration = 0.6 - else - if staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then - self._acceleration = 0 - else - self._acceleration = node_def._rail_acceleration or -0.4 + if statcdata.velocity < 1 then + staticdata.velocity = 1 end + acceleration = 2 + elseif self._fueltime and self._fueltime > 0 then + acceleration = 0.6 + elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then + acceleration = 0 + else + acceleration = node_def._rail_acceleration or -friction end - if self._acceleration > 0 and (staticdata.velocity or 0) < 0.1 then - staticdata.velocity = 0.1 - end - - if math.abs(self._acceleration) > 0.1 then - staticdata.velocity = ( staticdata.velocity or 0 ) + self._acceleration * timestep + if math.abs(acceleration) > (friction / 5) then + staticdata.velocity = ( staticdata.velocity or 0 ) + acceleration * timestep if staticdata.velocity > max_vel then staticdata.velocity = max_vel elseif staticdata.velocity < 0.1 then @@ -497,13 +505,48 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end end + -- Factor in gravity after everything else + local gravity_accel = 0 + local gravity_strength = friction + 0.2 + if staticdata.dir.y < 0 then + gravity_accel = gravity_strength + elseif staticdata.dir.y > 0 then + gravity_accel = -gravity_strength + end + if DEBUG and gravity_accel ~= 0 then + print("gravity_accel="..tostring(gravity_accel)) + end + if gravity_accel ~= 0 then + staticdata.velocity = (staticdata.velocity or 0) + gravity_accel + if staticdata.velocity < 0 then + if DEBUG then + print("Gravity flipped direction") + end + staticdata.velocity = staticdata.velocity * -1 + + -- Update direction + local next_dir,_ = mcl_minecarts:get_rail_direction(pos + staticdata.dir, staticdata.dir * -1, nil, nil, staticdata.railtype) + if DEBUG and next_dir ~= staticdata.dir then + print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) + end + staticdata.dir = next_dir + + update_cart_orientation(self,staticdata) + end + end + + -- Force the cart to stop if moving slowly enough + if (staticdata.velocity or 0) < 0.1 then + staticdata.velocity = 0 + end + if DEBUG and staticdata.velocity > 0 then - print( " acceleration="..tostring(self._acceleration)..",velocity="..tostring(staticdata.velocity).. - ",timestep="..tostring(timestep)) + print( " acceleration="..tostring(acceleration)..",velocity="..tostring(staticdata.velocity).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir)) end end - local function do_movement( self, dtime ) local staticdata = self._staticdata @@ -523,6 +566,11 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o process_acceleration(self,dtime * max_step_distance / total_distance) + -- Skip processing stopped railcarts + if not staticdata.velocity or math.abs(staticdata.velocity) < 0.05 then + return + end + while remaining_distance > 0.1 do local step_distance = do_movement_step(self, remaining_distance) if step_distance > 0.1 then From 6b37618b725a9c3266f99900022ad0b4d33d03e1 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Mar 2024 16:06:11 +0000 Subject: [PATCH 012/195] Remove dip into the ground that occured when gravity caused the cart to reverse directions --- mods/ENTITIES/mcl_minecarts/init.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 559ada225..253490b40 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -522,14 +522,13 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o if DEBUG then print("Gravity flipped direction") end - staticdata.velocity = staticdata.velocity * -1 - -- Update direction - local next_dir,_ = mcl_minecarts:get_rail_direction(pos + staticdata.dir, staticdata.dir * -1, nil, nil, staticdata.railtype) - if DEBUG and next_dir ~= staticdata.dir then - print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) - end - staticdata.dir = next_dir + -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at + staticdata.velocity = staticdata.velocity * -1 + staticdata.dir = -staticdata.dir + pos = pos + staticdata.dir + staticdata.distance = 1 - staticdata.distance + staticdata.connected_at = pos update_cart_orientation(self,staticdata) end From 5441a2357011f2daac55f11df7fc4f8344e1c4e9 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Mar 2024 17:28:23 +0000 Subject: [PATCH 013/195] Add code to reattach carts to rail when data corruption occurs, fix bug in last commit that caused carts to bury into the ground --- mods/ENTITIES/mcl_minecarts/init.lua | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 253490b40..ac8b3f225 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -272,8 +272,15 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local pos = staticdata.connected_at if not pos then pos = self.object:get_pos() - print("TODO: handle detached cart behavior") - --return + -- Try to reattach + local rounded_pos = vector.round(pos) + if mcl_minecarts:is_rail(rounded_pos) and vector.distance(pos, rounded_pos) < 0.25 then + -- Reattach + staticdata.connected_at = rounded_pos + pos = rounded_pos + else + print("TODO: handle detached cart behavior") + end end -- Fix railtype field @@ -525,11 +532,14 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at staticdata.velocity = staticdata.velocity * -1 - staticdata.dir = -staticdata.dir + next_dir = -staticdata.dir pos = pos + staticdata.dir staticdata.distance = 1 - staticdata.distance staticdata.connected_at = pos + local next_dir,_ = mcl_minecarts:get_rail_direction(pos + staticdata.dir, next_dir, nil, nil, staticdata.railtype) + staticdata.dir = next_dir + update_cart_orientation(self,staticdata) end end From bfe09c7c3f2d3a41071e4e04808fac46f81d7678 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Mar 2024 18:45:01 +0000 Subject: [PATCH 014/195] Move fiction constant to top of file, suppress cart flips when direction reverses due to gravity or end of track --- mods/ENTITIES/mcl_minecarts/init.lua | 45 +++++++++++++++++++--------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index ac8b3f225..a782741dc 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -8,6 +8,7 @@ mcl_minecarts.modpath = minetest.get_modpath(modname) mcl_minecarts.speed_max = 10 mcl_minecarts.check_float_time = 15 local max_step_distance = 0.5 +local friction = 0.1 dofile(mcl_minecarts.modpath.."/functions.lua") dofile(mcl_minecarts.modpath.."/rails.lua") @@ -371,26 +372,41 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o local passenger_attach_position = vector.new(0, -1.75, 0) local function update_cart_orientation(self,staticdata) - local rot = self.object:get_rotation() + -- constants + local _2_pi = math.pi * 2 + local pi = math.pi local dir = staticdata.dir - if dir.x < 0 then - rot.y = 0.5 * math.pi - elseif dir.x > 0 then - rot.y = 1.5 * math.pi - elseif dir.z < 0 then - rot.y = 1 * math.pi + + -- Calculate an angle from the x,z direction components + local rot_y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) + if rot_y < 0 then + rot_y = rot_y + _2_pi + end + + -- Check if the rotation is a 180 flip and don't change if so + local rot = self.object:get_rotation() + local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) + if diff < 0.001 or diff > _2_pi - 0.001 then + -- Update rotation adjust and recalculate the rotation + staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi + rot.y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) else - rot.y = 0 + rot.y = rot_y end -- Forward/backwards tilt (pitch) if dir.y < 0 then - rot.x = -0.25 * math.pi + rot.x = -0.25 * pi elseif dir.y > 0 then - rot.x = 0.25 * math.pi + rot.x = 0.25 * pi else rot.x = 0 end + + if ( staticdata.rot_adjust or 0 ) < 0.01 then + rot.x = -rot.x + end + self.object:set_rotation(rot) end @@ -473,7 +489,6 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o if not staticdata.connected_at then return end local acceleration = 0 - local friction = 0.1 if DEBUG and staticdata.velocity > 0 then print( " acceleration="..tostring(acceleration)..",velocity="..tostring(staticdata.velocity).. @@ -491,9 +506,6 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o elseif self._brake then acceleration = -1.5 elseif self._punched then - if statcdata.velocity < 1 then - staticdata.velocity = 1 - end acceleration = 2 elseif self._fueltime and self._fueltime > 0 then acceleration = 0.6 @@ -567,6 +579,11 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o staticdata.delay = 0 end + local initial_velocity = 1 + if self._punched and statcdata.velocity < initial_velocity then + staticdata.velocity = initial_velocity + end + -- Break long movements into fixed-size steps so that -- it is impossible to jump across gaps due to server lag -- causing large timesteps From 3745a27f8ce8364aa30330dd93ff679e38eb4bd3 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 10 Mar 2024 11:20:27 +0000 Subject: [PATCH 015/195] Rework in preparation to add code to pull from containers into the hopper minecart --- mods/ENTITIES/mcl_minecarts/init.lua | 711 ++++++++++++++------------- 1 file changed, 378 insertions(+), 333 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index a782741dc..33ff7dcfe 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -21,6 +21,265 @@ local function mcl_log(message) end end +local function handle_cart_enter(self,pos) + local node = minetest.get_node(pos) + + -- Handle track behaviors + local node_def = minetest.registered_nodes[node.name] + if node_def._mcl_minecarts_on_enter then + node_def._mcl_minecarts_on_enter(pos, cart) + end + + -- Handle above-track behaviors (to ensure hoppers can transfer at least one item) + pos = pos + vector.new(0,1,0) + local node = minetest.get_node(pos) + if node_def._mcl_minecarts_on_enter_below then + node_def._mcl_minecarts_on_enter_below(pos, cart) + end + + -- Handle cart-specific behaviors + if self._mcl_minecarts_on_enter then + self._mcl_minecarts_on_enter(self, pos) + end +end + +local function handle_cart_leave(pos) + local node_name = minetest.get_node(pos).name + local node_def = minetest.registered_nodes[node_name] + if node_def._mcl_minecarts_on_leave then + node_def._mcl_minecarts_on_leave(pos, self) + end +end + +local function update_cart_orientation(self,staticdata) + -- constants + local _2_pi = math.pi * 2 + local pi = math.pi + local dir = staticdata.dir + + -- Calculate an angle from the x,z direction components + local rot_y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) + if rot_y < 0 then + rot_y = rot_y + _2_pi + end + + -- Check if the rotation is a 180 flip and don't change if so + local rot = self.object:get_rotation() + local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) + if diff < 0.001 or diff > _2_pi - 0.001 then + -- Update rotation adjust and recalculate the rotation + staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi + rot.y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) + else + rot.y = rot_y + end + + -- Forward/backwards tilt (pitch) + if dir.y < 0 then + rot.x = -0.25 * pi + elseif dir.y > 0 then + rot.x = 0.25 * pi + else + rot.x = 0 + end + + if ( staticdata.rot_adjust or 0 ) < 0.01 then + rot.x = -rot.x + end + + self.object:set_rotation(rot) +end + +local function do_movement_step(self, remaining_distance) + local staticdata = self._staticdata + + local pos = staticdata.connected_at + + if not pos then return remaining_distance end + if staticdata.velocity < 0.1 then return remaining_distance end + + local remaining_in_block = 1 - ( staticdata.distance or 0 ) + local dinstance = 0 + if remaining_in_block > remaining_distance then + distance = remaining_distance + staticdata.distance = ( staticdata.distance or 0 ) + distance + pos = pos + staticdata.dir * staticdata.distance + else + distance = remaining_in_block + staticdata.distance = 0 + + -- Leave the old node + handle_cart_leave(pos) + + -- Anchor at the next node + pos = pos + staticdata.dir + staticdata.connected_at = pos + + -- Enter the new node + handle_cart_enter(self,pos) + + -- check for hopper under the rail + local under_pos = pos - vector.new(0,1,0) + local under_node_name = minetest.get_node(under_pos).name + local under_node_def = minetest.registered_nodes[under_node_name] + if DEBUG then print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) end + if under_node_def and under_node_def.groups.hopper ~= 0 then + if DEBUG then print( "Attempt pull_from_minecart" ) end + if mcl_hoppers.pull_from_minecart( self, under_pos, self._inv_size or 0 ) then + staticdata.delay = 1.5 + end + end + + -- Get the next direction + local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) + if DEBUG and next_dir ~= staticdata.dir then + print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) + end + + -- Handle end of track + if next_dir == staticdata.dir * -1 then + if DEBUG then print("Stopping cart at "..tostring(pos)) end + staticdata.velocity = 0 + distence = remaining_distance + end + + -- Update cart direction + staticdata.dir = next_dir + end + + self.object:move_to(pos) + + -- Update cart orientation + update_cart_orientation(self,staticdata) + + -- Report distance traveled + return distance +end + +local function process_acceleration(self, timestep) + local staticdata = self._staticdata + if not staticdata.connected_at then return end + + local acceleration = 0 + + if DEBUG and staticdata.velocity > 0 then + print( " acceleration="..tostring(acceleration)..",velocity="..tostring(staticdata.velocity).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir)) + end + + local pos = staticdata.connected_at + local node_name = minetest.get_node(pos).name + local node_def = minetest.registered_nodes[node_name] + local max_vel = mcl_minecarts.speed_max + + if self._go_forward then + acceleration = 2 + elseif self._brake then + acceleration = -1.5 + elseif self._punched then + acceleration = 2 + elseif self._fueltime and self._fueltime > 0 then + acceleration = 0.6 + elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then + acceleration = 0 + else + acceleration = node_def._rail_acceleration or -friction + end + + if math.abs(acceleration) > (friction / 5) then + staticdata.velocity = ( staticdata.velocity or 0 ) + acceleration * timestep + if staticdata.velocity > max_vel then + staticdata.velocity = max_vel + elseif staticdata.velocity < 0.1 then + staticdata.velocity = 0 + end + end + + -- Factor in gravity after everything else + local gravity_accel = 0 + local gravity_strength = friction + 0.2 + if staticdata.dir.y < 0 then + gravity_accel = gravity_strength + elseif staticdata.dir.y > 0 then + gravity_accel = -gravity_strength + end + if DEBUG and gravity_accel ~= 0 then + print("gravity_accel="..tostring(gravity_accel)) + end + if gravity_accel ~= 0 then + staticdata.velocity = (staticdata.velocity or 0) + gravity_accel + if staticdata.velocity < 0 then + if DEBUG then + print("Gravity flipped direction") + end + + -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at + staticdata.velocity = staticdata.velocity * -1 + next_dir = -staticdata.dir + pos = pos + staticdata.dir + staticdata.distance = 1 - staticdata.distance + staticdata.connected_at = pos + + local next_dir,_ = mcl_minecarts:get_rail_direction(pos + staticdata.dir, next_dir, nil, nil, staticdata.railtype) + staticdata.dir = next_dir + + update_cart_orientation(self,staticdata) + end + end + + -- Force the cart to stop if moving slowly enough + if (staticdata.velocity or 0) < 0.1 then + staticdata.velocity = 0 + end + + if DEBUG and staticdata.velocity > 0 then + print( " acceleration="..tostring(acceleration)..",velocity="..tostring(staticdata.velocity).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir)) + end +end + +local function do_movement( self, dtime ) + local staticdata = self._staticdata + + -- Allow the carts to be delay for the rest of the world to react before moving again + if ( staticdata.delay or 0 ) > dtime then + staticdata.delay = staticdata.delay - dtime + return + else + staticdata.delay = 0 + end + + local initial_velocity = 1 + if self._punched and statcdata.velocity < initial_velocity then + staticdata.velocity = initial_velocity + end + + -- Break long movements into fixed-size steps so that + -- it is impossible to jump across gaps due to server lag + -- causing large timesteps + local total_distance = dtime * ( staticdata.velocity or 0 ) + local remaining_distance = total_distance + + process_acceleration(self,dtime * max_step_distance / total_distance) + + -- Skip processing stopped railcarts + if not staticdata.velocity or math.abs(staticdata.velocity) < 0.05 then + return + end + + while remaining_distance > 0.1 do + local step_distance = do_movement_step(self, remaining_distance) + if step_distance > 0.1 then + process_acceleration(self, dtime * step_distance / total_distance) + end + remaining_distance = remaining_distance - step_distance + end + + -- Clear punched flag now that movement for this step has been completed + self._punched = false +end local function detach_driver(self) if not self._driver then @@ -211,18 +470,21 @@ local function to_dirstring(dir) end end -local function register_entity(entity_id, mesh, textures, drop, on_rightclick, on_activate_by_rail) +local function register_entity(entity_id, def) + assert( def.drop, "drop is required parameter" ) local cart = { initial_properties = { physical = false, collisionbox = {-10/16., -0.5, -10/16, 10/16, 0.25, 10/16}, visual = "mesh", - mesh = mesh, + mesh = def.mesh, visual_size = {x=1, y=1}, - textures = textures, + textures = def.textures, }, - on_rightclick = on_rightclick, + on_rightclick = def.on_rightclick, + on_activate_by_rail = def.on_activate_by_rail, + _mcl_minecarts_on_enter = def._mcl_minecarts_on_enter, _driver = nil, -- player who sits in and controls the minecart (only for minecart!) _passenger = nil, -- for mobs @@ -321,6 +583,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end -- Drop items and remove cart entity + local drop = def.drop if not minetest.is_creative_enabled(puncher:get_player_name()) then for d=1, #drop do minetest.add_item(self.object:get_pos(), drop[d]) @@ -367,248 +630,8 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o end end - cart.on_activate_by_rail = on_activate_by_rail - local passenger_attach_position = vector.new(0, -1.75, 0) - local function update_cart_orientation(self,staticdata) - -- constants - local _2_pi = math.pi * 2 - local pi = math.pi - local dir = staticdata.dir - - -- Calculate an angle from the x,z direction components - local rot_y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) - if rot_y < 0 then - rot_y = rot_y + _2_pi - end - - -- Check if the rotation is a 180 flip and don't change if so - local rot = self.object:get_rotation() - local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) - if diff < 0.001 or diff > _2_pi - 0.001 then - -- Update rotation adjust and recalculate the rotation - staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi - rot.y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) - else - rot.y = rot_y - end - - -- Forward/backwards tilt (pitch) - if dir.y < 0 then - rot.x = -0.25 * pi - elseif dir.y > 0 then - rot.x = 0.25 * pi - else - rot.x = 0 - end - - if ( staticdata.rot_adjust or 0 ) < 0.01 then - rot.x = -rot.x - end - - self.object:set_rotation(rot) - end - - local function do_movement_step(self, remaining_distance) - local staticdata = self._staticdata - - local pos = staticdata.connected_at - - if not pos then return remaining_distance end - if staticdata.velocity < 0.1 then return remaining_distance end - - local remaining_in_block = 1 - ( staticdata.distance or 0 ) - local dinstance = 0 - if remaining_in_block > remaining_distance then - distance = remaining_distance - staticdata.distance = ( staticdata.distance or 0 ) + distance - pos = pos + staticdata.dir * staticdata.distance - else - distance = remaining_in_block - staticdata.distance = 0 - - -- Leave the old node - local old_node_name = minetest.get_node(pos).name - local old_node_def = minetest.registered_nodes[old_node_name] - if old_node_def._mcl_minecarts_on_leave then - old_node_def._mcl_minecarts_on_leave( pos, self ) - end - - -- Anchor at the next node - pos = pos + staticdata.dir - staticdata.connected_at = pos - - -- Enter the new node - local new_node_name = minetest.get_node(pos).name - local new_node_def = minetest.registered_nodes[new_node_name] - if new_node_def._mcl_minecarts_on_enter then - new_node_def._mcl_minecarts_on_enter( pos, self ) - end - - -- check for hopper under the rail - local under_pos = pos - vector.new(0,1,0) - local under_node_name = minetest.get_node(under_pos).name - local under_node_def = minetest.registered_nodes[under_node_name] - if DEBUG then print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) end - if under_node_def and under_node_def.groups.hopper ~= 0 then - if DEBUG then print( "Attempt pull_from_minecart" ) end - if mcl_hoppers.pull_from_minecart( self, under_pos, self._inv_size or 0 ) then - staticdata.delay = 1.5 - end - end - - -- Get the next direction - local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) - if DEBUG and next_dir ~= staticdata.dir then - print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) - end - - -- Handle end of track - if next_dir == staticdata.dir * -1 then - if DEBUG then print("Stopping cart at "..tostring(pos)) end - staticdata.velocity = 0 - distence = remaining_distance - end - - -- Update cart direction - staticdata.dir = next_dir - end - - self.object:move_to(pos) - - -- Update cart orientation - update_cart_orientation(self,staticdata) - - -- Report distance traveled - return distance - end - - local function process_acceleration(self, timestep) - local staticdata = self._staticdata - if not staticdata.connected_at then return end - - local acceleration = 0 - - if DEBUG and staticdata.velocity > 0 then - print( " acceleration="..tostring(acceleration)..",velocity="..tostring(staticdata.velocity).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir)) - end - - local pos = staticdata.connected_at - local node_name = minetest.get_node(pos).name - local node_def = minetest.registered_nodes[node_name] - local max_vel = mcl_minecarts.speed_max - - if self._go_forward then - acceleration = 2 - elseif self._brake then - acceleration = -1.5 - elseif self._punched then - acceleration = 2 - elseif self._fueltime and self._fueltime > 0 then - acceleration = 0.6 - elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then - acceleration = 0 - else - acceleration = node_def._rail_acceleration or -friction - end - - if math.abs(acceleration) > (friction / 5) then - staticdata.velocity = ( staticdata.velocity or 0 ) + acceleration * timestep - if staticdata.velocity > max_vel then - staticdata.velocity = max_vel - elseif staticdata.velocity < 0.1 then - staticdata.velocity = 0 - end - end - - -- Factor in gravity after everything else - local gravity_accel = 0 - local gravity_strength = friction + 0.2 - if staticdata.dir.y < 0 then - gravity_accel = gravity_strength - elseif staticdata.dir.y > 0 then - gravity_accel = -gravity_strength - end - if DEBUG and gravity_accel ~= 0 then - print("gravity_accel="..tostring(gravity_accel)) - end - if gravity_accel ~= 0 then - staticdata.velocity = (staticdata.velocity or 0) + gravity_accel - if staticdata.velocity < 0 then - if DEBUG then - print("Gravity flipped direction") - end - - -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at - staticdata.velocity = staticdata.velocity * -1 - next_dir = -staticdata.dir - pos = pos + staticdata.dir - staticdata.distance = 1 - staticdata.distance - staticdata.connected_at = pos - - local next_dir,_ = mcl_minecarts:get_rail_direction(pos + staticdata.dir, next_dir, nil, nil, staticdata.railtype) - staticdata.dir = next_dir - - update_cart_orientation(self,staticdata) - end - end - - -- Force the cart to stop if moving slowly enough - if (staticdata.velocity or 0) < 0.1 then - staticdata.velocity = 0 - end - - if DEBUG and staticdata.velocity > 0 then - print( " acceleration="..tostring(acceleration)..",velocity="..tostring(staticdata.velocity).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir)) - end - end - - local function do_movement( self, dtime ) - local staticdata = self._staticdata - - -- Allow the carts to be delay for the rest of the world to react before moving again - if ( staticdata.delay or 0 ) > dtime then - staticdata.delay = staticdata.delay - dtime - return - else - staticdata.delay = 0 - end - - local initial_velocity = 1 - if self._punched and statcdata.velocity < initial_velocity then - staticdata.velocity = initial_velocity - end - - -- Break long movements into fixed-size steps so that - -- it is impossible to jump across gaps due to server lag - -- causing large timesteps - local total_distance = dtime * ( staticdata.velocity or 0 ) - local remaining_distance = total_distance - - process_acceleration(self,dtime * max_step_distance / total_distance) - - -- Skip processing stopped railcarts - if not staticdata.velocity or math.abs(staticdata.velocity) < 0.05 then - return - end - - while remaining_distance > 0.1 do - local step_distance = do_movement_step(self, remaining_distance) - if step_distance > 0.1 then - process_acceleration(self, dtime * step_distance / total_distance) - end - remaining_distance = remaining_distance - step_distance - end - - -- Clear punched flag now that movement for this step has been completed - self._punched = false - end - function cart:on_step(dtime) hopper_take_item(self, dtime) local staticdata = self._staticdata @@ -652,6 +675,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then detach_driver(self) + local drop = def.drop for d = 1, #drop do minetest.add_item(pos, drop[d]) end @@ -811,11 +835,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) le._staticdata = make_staticdata( railtype, railpos, cart_dir ) end - -- Handle track behaviors - local node_def = minetest.registered_nodes[node.name] - if node_def._mcl_minecarts_on_enter then - node_def._mcl_minecarts_on_enter(railpos, cart) - end + handle_cart_enter( railpos ) local pname = "" if placer then @@ -891,30 +911,30 @@ Register a minecart * on_activate_by_rail: Called when above activator rail * creative: If false, don't show in Creative Inventory ]] -local function register_minecart(itemstring, entity_id, description, tt_help, longdesc, usagehelp, mesh, textures, icon, drop, on_rightclick, on_activate_by_rail, creative) - register_entity(entity_id, mesh, textures, drop, on_rightclick, on_activate_by_rail) - register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative) +local function register_minecart(def) + register_entity(def.entity_id, def) + register_craftitem(def.itemstring, def.entity_id, def.description, def.tt_help, def.longdesc, def.usagehelp, def.icon, def.creative) if minetest.get_modpath("doc_identifier") then - doc.sub.identifier.register_object(entity_id, "craftitems", itemstring) + doc.sub.identifier.register_object(def.entity_id, "craftitems", itemstring) end end -- Minecart -register_minecart( - "mcl_minecarts:minecart", - "mcl_minecarts:minecart", - S("Minecart"), - S("Vehicle for fast travel on rails"), - S("Minecarts can be used for a quick transportion on rails.") .. "\n" .. - S("Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type."), - S("You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.") .. "\n" .. - S("To obtain the minecart, punch it while holding down the sneak key.") .. "\n" .. - S("If it moves over a powered activator rail, you'll get ejected."), - "mcl_minecarts_minecart.b3d", - {"mcl_minecarts_minecart.png"}, - "mcl_minecarts_minecart_normal.png", - {"mcl_minecarts:minecart"}, - function(self, clicker) +register_minecart({ + itemstring = "mcl_minecarts:minecart", + entity_id = "mcl_minecarts:minecart", + description = S("Minecart"), + tt_helop = S("Vehicle for fast travel on rails"), + long_descp = S("Minecarts can be used for a quick transportion on rails.") .. "\n" .. + S("Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type."), + S("You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.") .. "\n" .. + S("To obtain the minecart, punch it while holding down the sneak key.") .. "\n" .. + S("If it moves over a powered activator rail, you'll get ejected."), + mesh = "mcl_minecarts_minecart.b3d", + textures = {"mcl_minecarts_minecart.png"}, + icon = "mcl_minecarts_minecart_normal.png", + drop = {"mcl_minecarts:minecart"}, + on_rightclick = function(self, clicker) local name = clicker:get_player_name() if not clicker or not clicker:is_player() then return @@ -937,34 +957,43 @@ register_minecart( end end, name) end - end, activate_normal_minecart -) + end, + on_activate_by_rail = activate_normal_minecart +}) -- Minecart with Chest -register_minecart( - "mcl_minecarts:chest_minecart", - "mcl_minecarts:chest_minecart", - S("Minecart with Chest"), - nil, nil, nil, - "mcl_minecarts_minecart_chest.b3d", - { "mcl_chests_normal.png", "mcl_minecarts_minecart.png" }, - "mcl_minecarts_minecart_chest.png", - {"mcl_minecarts:minecart", "mcl_chests:chest"}, - nil, nil, true) +register_minecart({ + itemstring = "mcl_minecarts:chest_minecart", + entity_id = "mcl_minecarts:chest_minecart", + description = S("Minecart with Chest"), + tt_help = nil, + longdesc = nil, + usagehelp = nil, + mesh = "mcl_minecarts_minecart_chest.b3d", + textures = { + "mcl_chests_normal.png", + "mcl_minecarts_minecart.png" + }, + icon = "mcl_minecarts_minecart_chest.png", + drop = {"mcl_minecarts:minecart", "mcl_chests:chest"}, + on_rightclick = nil, + on_activate_by_rail = nil, + creative = true +}) mcl_entity_invs.register_inv("mcl_minecarts:chest_minecart","Minecart",27,false,true) -- Minecart with Furnace -register_minecart( - "mcl_minecarts:furnace_minecart", - "mcl_minecarts:furnace_minecart", - S("Minecart with Furnace"), - nil, - S("A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel."), - S("Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.") .. "\n" .. - S("To obtain the minecart and furnace, punch them while holding down the sneak key."), +register_minecart({ + itemstring = "mcl_minecarts:furnace_minecart", + entity_id = "mcl_minecarts:furnace_minecart", + description = S("Minecart with Furnace"), + tt_help = nil, + longdesc = S("A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel."), + usagehelp = S("Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.") .. "\n" .. + S("To obtain the minecart and furnace, punch them while holding down the sneak key."), - "mcl_minecarts_minecart_block.b3d", - { + mesh = "mcl_minecarts_minecart_block.b3d", + textures = { "default_furnace_top.png", "default_furnace_top.png", "default_furnace_front.png", @@ -973,10 +1002,10 @@ register_minecart( "default_furnace_side.png", "mcl_minecarts_minecart.png", }, - "mcl_minecarts_minecart_furnace.png", - {"mcl_minecarts:minecart", "mcl_furnaces:furnace"}, - -- Feed furnace with coal - function(self, clicker) + icon = "mcl_minecarts_minecart_furnace.png", + drop = {"mcl_minecarts:minecart", "mcl_furnaces:furnace"}, + on_rightclick = function(self, clicker) + -- Feed furnace with coal if not clicker or not clicker:is_player() then return end @@ -1004,17 +1033,21 @@ register_minecart( "mcl_minecarts_minecart.png", }}) end - end, nil, true -) + end, + on_activate_by_rail = nil, + creative = true +}) -- Minecart with Command Block -register_minecart( - "mcl_minecarts:command_block_minecart", - "mcl_minecarts:command_block_minecart", - S("Minecart with Command Block"), - nil, nil, nil, - "mcl_minecarts_minecart_block.b3d", - { +register_minecart({ + itemstring = "mcl_minecarts:command_block_minecart", + entity_id = "mcl_minecarts:command_block_minecart", + description = S("Minecart with Command Block"), + tt_help = nil, + loncdesc = nil, + usagehelp = nil, + mesh = "mcl_minecarts_minecart_block.b3d", + textures = { "jeija_commandblock_off.png^[verticalframe:2:0", "jeija_commandblock_off.png^[verticalframe:2:0", "jeija_commandblock_off.png^[verticalframe:2:0", @@ -1023,41 +1056,50 @@ register_minecart( "jeija_commandblock_off.png^[verticalframe:2:0", "mcl_minecarts_minecart.png", }, - "mcl_minecarts_minecart_command_block.png", - {"mcl_minecarts:minecart"}, - nil, nil, false -) + icon = "mcl_minecarts_minecart_command_block.png", + drop = {"mcl_minecarts:minecart"}, + on_rightclick = nil, + on_activate_by_rail = nil, + creative = false +}) -- Minecart with Hopper -register_minecart( - "mcl_minecarts:hopper_minecart", - "mcl_minecarts:hopper_minecart", - S("Minecart with Hopper"), - nil, nil, nil, - "mcl_minecarts_minecart_hopper.b3d", - { +register_minecart({ + itemstring = "mcl_minecarts:hopper_minecart", + entity_id = "mcl_minecarts:hopper_minecart", + description = S("Minecart with Hopper"), + tt_help = nil, + longdesc = nil, + usagehelp = nil, + mesh = "mcl_minecarts_minecart_hopper.b3d", + textures = { "mcl_hoppers_hopper_inside.png", "mcl_minecarts_minecart.png", "mcl_hoppers_hopper_outside.png", "mcl_hoppers_hopper_top.png", }, - "mcl_minecarts_minecart_hopper.png", - {"mcl_minecarts:minecart", "mcl_hoppers:hopper"}, - nil, nil, true -) + icon = "mcl_minecarts_minecart_hopper.png", + drop = {"mcl_minecarts:minecart", "mcl_hoppers:hopper"}, + on_rightclick = nil, + on_activate_by_rail = nil, + _mcl_minecarts_on_enter = function(self,pos) + -- TODO: try to pull from containers into our inventory + end, + creative = true +}) mcl_entity_invs.register_inv("mcl_minecarts:hopper_minecart", "Hopper Minecart", 5, false, true) -- Minecart with TNT -register_minecart( - "mcl_minecarts:tnt_minecart", - "mcl_minecarts:tnt_minecart", - S("Minecart with TNT"), - S("Vehicle for fast travel on rails").."\n"..S("Can be ignited by tools or powered activator rail"), - S("A minecart with TNT is an explosive vehicle that travels on rail."), - S("Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.") .. "\n" .. - S("To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited."), - "mcl_minecarts_minecart_block.b3d", - { +register_minecart({ + itemstring = "mcl_minecarts:tnt_minecart", + entity_id = "mcl_minecarts:tnt_minecart", + description = S("Minecart with TNT"), + tt_help = S("Vehicle for fast travel on rails").."\n"..S("Can be ignited by tools or powered activator rail"), + longdesc = S("A minecart with TNT is an explosive vehicle that travels on rail."), + usagehelp = S("Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.") .. "\n" .. + S("To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited."), + mesh = "mcl_minecarts_minecart_block.b3d", + textures = { "default_tnt_top.png", "default_tnt_bottom.png", "default_tnt_side.png", @@ -1066,10 +1108,10 @@ register_minecart( "default_tnt_side.png", "mcl_minecarts_minecart.png", }, - "mcl_minecarts_minecart_tnt.png", - {"mcl_minecarts:minecart", "mcl_tnt:tnt"}, - -- Ingite - function(self, clicker) + icon = "mcl_minecarts_minecart_tnt.png", + drop = {"mcl_minecarts:minecart", "mcl_tnt:tnt"}, + on_rightclick = function(self, clicker) + -- Ingite if not clicker or not clicker:is_player() then return end @@ -1086,7 +1128,10 @@ register_minecart( end activate_tnt_minecart(self) end - end, activate_tnt_minecart) + end, + on_activate_by_rail = activate_tnt_minecart, + creative = false +}) minetest.register_craft({ From 121550a6359ecef981ae34ae9fd99c71bffc319b Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 11 Mar 2024 00:34:26 +0000 Subject: [PATCH 016/195] Hopper minecarts pull from containers above rail --- mods/CORE/mcl_util/init.lua | 27 +++++++++++++++++++++++++++ mods/ENTITIES/mcl_minecarts/init.lua | 25 +++++++++++++++++++------ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index b9c510390..229c9c92a 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -231,6 +231,33 @@ function mcl_util.hopper_push(pos, dst_pos) return ok end +function mcl_util.hopper_pull_to_inventory(hop_inv, hop_list, src_pos) + -- TODO: merge together with hopper_pull after https://git.minetest.land/MineClone2/MineClone2/pulls/4190 is merged + -- Get node pos' for item transfer + local src = minetest.get_node(src_pos) + if not minetest.registered_nodes[src.name] then return end + local src_type = minetest.get_item_group(src.name, "container") + if src_type ~= 2 then return end + local src_def = minetest.registered_nodes[src.name] + + local src_list = 'main' + local src_inv, stack_id + + if src_def._mcl_hoppers_on_try_pull then + src_inv, src_list, stack_id = src_def._mcl_hoppers_on_try_pull(src_pos, pos, hop_inv, hop_list) + else + local src_meta = minetest.get_meta(src_pos) + src_inv = src_meta:get_inventory() + stack_id = mcl_util.select_stack(src_inv, src_list, hop_inv, hop_list) + end + + if stack_id ~= nil then + local ok = mcl_util.move_item(src_inv, src_list, stack_id, hop_inv, hop_list) + if src_def._mcl_hoppers_on_after_pull then + src_def._mcl_hoppers_on_after_pull(src_pos) + end + end +end -- Try pulling from source inventory to hopper inventory ---@param pos Vector ---@param src_pos Vector diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 33ff7dcfe..1346e502b 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -31,10 +31,10 @@ local function handle_cart_enter(self,pos) end -- Handle above-track behaviors (to ensure hoppers can transfer at least one item) - pos = pos + vector.new(0,1,0) - local node = minetest.get_node(pos) + local above_pos = pos + vector.new(0,1,0) + local node = minetest.get_node(above_pos) if node_def._mcl_minecarts_on_enter_below then - node_def._mcl_minecarts_on_enter_below(pos, cart) + node_def._mcl_minecarts_on_enter_below(above_pos, cart) end -- Handle cart-specific behaviors @@ -116,7 +116,7 @@ local function do_movement_step(self, remaining_distance) staticdata.connected_at = pos -- Enter the new node - handle_cart_enter(self,pos) + handle_cart_enter(self, pos) -- check for hopper under the rail local under_pos = pos - vector.new(0,1,0) @@ -639,6 +639,9 @@ local function register_entity(entity_id, def) staticdata = make_staticdata() self._staticdata = staticdata end + if (staticdata.hopper_delay or 0) > 0 then + staticdata.hopper_delay = staticdata.hopper_delay - dtime + end local pos, rou_pos, node = self.object:get_pos() --local update = {} @@ -835,7 +838,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) le._staticdata = make_staticdata( railtype, railpos, cart_dir ) end - handle_cart_enter( railpos ) + handle_cart_enter(le, railpos) local pname = "" if placer then @@ -1083,7 +1086,17 @@ register_minecart({ on_rightclick = nil, on_activate_by_rail = nil, _mcl_minecarts_on_enter = function(self,pos) - -- TODO: try to pull from containers into our inventory + local staticdata = self._staticdata + if (staticdata.hopper_delay or 0) > 0 then + return + end + + -- try to pull from containers into our inventory + local inv = mcl_entity_invs.load_inv(self,5) + local above_pos = pos + vector.new(0,1,0) + mcl_util.hopper_pull_to_inventory(inv, 'main', above_pos) + + staticdata.hopper_delay = (staticdata.hopper_delay or 0) + (1/20) end, creative = true }) From 15584dad88fabefd9f6a6bde760932fbfdde5769 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 11 Mar 2024 07:26:51 +0000 Subject: [PATCH 017/195] Implement custom item dropper handlers, implement droppers placing minecarts --- mods/ENTITIES/mcl_minecarts/init.lua | 14 ++++++++ mods/ITEMS/REDSTONE/mcl_droppers/init.lua | 41 ++++++++++++++++++----- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 1346e502b..6d31a1fa1 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -850,6 +850,19 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) return itemstack end +local function dropper_place_minecart(dropitem, pos) + local node = minetest.get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + + -- Don't try to place the minecart if pos isn't a rail + if (nodedef.groups.rail or 0) == 0 then return false end + + mcl_minecarts.place_minecart(dropitem, { + above = pos, + under = vector.offset(pos,0,-1,0) + }) + return true +end local function register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative) entity_mapping[itemstring] = entity_id @@ -860,6 +873,7 @@ local function register_craftitem(itemstring, entity_id, description, tt_help, l end local def = { stack_max = 1, + _mcl_dropper_on_drop = dropper_place_minecart, on_place = function(itemstack, placer, pointed_thing) if not pointed_thing.type == "node" then return diff --git a/mods/ITEMS/REDSTONE/mcl_droppers/init.lua b/mods/ITEMS/REDSTONE/mcl_droppers/init.lua index 625549a1d..13a5b879d 100644 --- a/mods/ITEMS/REDSTONE/mcl_droppers/init.lua +++ b/mods/ITEMS/REDSTONE/mcl_droppers/init.lua @@ -132,9 +132,9 @@ local dropperdef = { -- If they are containers - double down as hopper mcl_util.hopper_push(pos, droppos) end - if dropnodedef.walkable then - return - end + if dropnodedef.walkable then return end + + -- Build a list of items in the dropper local stacks = {} for i = 1, inv:get_size("main") do local stack = inv:get_stack("main", i) @@ -142,17 +142,36 @@ local dropperdef = { table.insert(stacks, { stack = stack, stackpos = i }) end end + + -- Pick an item to drop + local dropitem = nil + local stack = nil + local r = nil if #stacks >= 1 then - local r = math.random(1, #stacks) - local stack = stacks[r].stack - local dropitem = ItemStack(stack) + r = math.random(1, #stacks) + stack = stacks[r].stack + dropitem = ItemStack(stack) local stackdef = core.registered_items[stack:get_name()] if not stackdef then return end - dropitem:set_count(1) - local stack_id = stacks[r].stackpos + end + if not dropitem then return end + + -- Flag for if the item was dropped. If true the item will be removed from + -- the inventory after dropping + local item_dropped = false + + -- Check if the drop item has a custom handler + local itemdef = minetest.registered_craftitems[dropitem:get_name()] + if itemdef._mcl_dropper_on_drop then + item_dropped = itemdef._mcl_dropper_on_drop(dropitem, droppos) + end + + -- If a custom handler wasn't successful then drop the item as an entity + if not item_dropped then + -- Drop as entity local pos_variation = 100 droppos = vector.offset(droppos, math.random(-pos_variation, pos_variation) / 1000, @@ -164,6 +183,12 @@ local dropperdef = { local speed = 3 item_entity:set_velocity(vector.multiply(drop_vel, speed)) stack:take_item() + item_dropped = trie + end + + -- Remove dropped items from inventory + if item_dropped then + local stack_id = stacks[r].stackpos inv:set_stack("main", stack_id, stack) end end, From acd6b033715979e2c2963d39cc72c7d5429a6696 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 11 Mar 2024 08:13:36 +0000 Subject: [PATCH 018/195] Make TNT minecarts available in creative menu --- mods/ENTITIES/mcl_minecarts/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 6d31a1fa1..d5293724b 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -1157,7 +1157,7 @@ register_minecart({ end end, on_activate_by_rail = activate_tnt_minecart, - creative = false + creative = true }) From 262063cc7c6974f219c1053db4060c6e0e107cb2 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 11 Mar 2024 08:57:43 +0000 Subject: [PATCH 019/195] Add diagonal track movement on zig-zag track, rewrite mcl_minecarts:get_rail_direction --- mods/ENTITIES/mcl_minecarts/functions.lua | 134 ++++++++++------------ 1 file changed, 60 insertions(+), 74 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 587bbd281..9ea7de40a 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -46,90 +46,76 @@ function mcl_minecarts:is_rail(pos, railtype) return minetest.get_item_group(node, "connect_to_raillike") == railtype end -function mcl_minecarts:check_front_up_down(pos, dir_, check_down, railtype) - local dir = vector.new(dir_) - -- Front - dir.y = 0 - local cur = vector.add(pos, dir) - if mcl_minecarts:is_rail(cur, railtype) then - return dir +--[[ + Returns a string description of a direction, with optional _up/_down suffix +]] +function mcl_minecarts:name_from_dir(dir, vertical) + local res = "" + + if dir.z == 1 then res = res .. "n" end + if dir.z == -1 then res = res .. "s" end + + if dir.x == -1 then res = res .. "w" end + if dir.x == 1 then res = res .. "e" end + + if vertical then + if dir.y == 1 then res = res .. "_up" end + if dir.y == 1 then res = res .. "_down" end end - -- Up - if check_down then - dir.y = 1 - cur = vector.add(pos, dir) - if mcl_minecarts:is_rail(cur, railtype) then - return dir - end - end - -- Down - dir.y = -1 - cur = vector.add(pos, dir) - if mcl_minecarts:is_rail(cur, railtype) then - return dir - end - return nil + + return res end + +--[[ + An array of (u,v,w) positions to check. Actual direction is u * dir + v * right + w * vector.new(0,1,0) +]] +local rail_checks = { + { 1, 0, 0 }, -- forwards + { 1, 0, 1 }, -- forwards and up + { 1, 0, -1 }, -- forwards and down + + { 1, 1, 0 }, -- diagonal left + { 0, 1, 0 }, -- left + { 0, 1, 1 }, -- left and up + { 0, 1, -1 }, -- left and down + + { 1, -1, 0 }, -- diagonal right + { 0, -1, 0 }, -- right + { 0, -1, 1 }, -- right and up + { 0, -1, -1 }, -- right and down + + { -1, 0, 0 }, -- backwards +} + +-- Rotate diagonal directions 45 degrees clockwise +local diagonal_convert = { + nw = vector.new( 0,0, 1), -- north + ne = vector.new( 1,0, 0), -- east + se = vector.new( 0,0,-1), -- south + sw = vector.new(-1,0, 0), -- west +} + function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) local pos = vector.round(pos_) - local cur - local left_check, right_check = true, true - -- Calculate left, right and back - local left = vector.new(-dir.z, dir.y, dir.x) + -- Diagonal conversion + if dir.x ~= 0 and dir.z ~= 0 then + dir = diagonal_convert[ mcl_minecarts:name_from_dir(dir, false) ] + end + + -- Calculate coordinate space local right = vector.new( dir.z, dir.y, -dir.x) - local back = vector.new(-dir.x, dir.y, -dir.z) + local up = vector.new(0,1,0) - if ctrl then - if old_switch == 1 then - left_check = false - elseif old_switch == 2 then - right_check = false - end - if ctrl.left and left_check then - cur = mcl_minecarts:check_front_up_down(pos, left, false, railtype) - if cur then - return cur, 1 - end - left_check = false - end - if ctrl.right and right_check then - cur = mcl_minecarts:check_front_up_down(pos, right, false, railtype) - if cur then - return cur, 2 - end - right_check = true + -- Perform checks + for _,check in ipairs(rail_checks) do + local check_dir = dir * check[1] + right * check[2] + up * check[3] + local check_pos = pos + check_dir + if mcl_minecarts:is_rail(check_pos,railtype) then + return check_dir end end - -- Normal - cur = mcl_minecarts:check_front_up_down(pos, dir, true, railtype) - if cur then - return cur - end - - -- Left, if not already checked - if left_check then - cur = mcl_minecarts:check_front_up_down(pos, left, false, railtype) - if cur then - return cur - end - end - - -- Right, if not already checked - if right_check then - cur = mcl_minecarts:check_front_up_down(pos, right, false, railtype) - if cur then - return cur - end - end - -- Backwards - if not old_switch then - cur = mcl_minecarts:check_front_up_down(pos, back, true, railtype) - if cur then - return cur - end - end return vector.new(0,0,0) end From 4656717c56b3a9d6b15e88b32dd4e27610f5ce16 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 11 Mar 2024 20:14:11 +0000 Subject: [PATCH 020/195] Fix diagonal movement --- mods/ENTITIES/mcl_minecarts/functions.lua | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 9ea7de40a..48392cbee 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -88,20 +88,33 @@ local rail_checks = { { -1, 0, 0 }, -- backwards } +local rail_checks_diagonal = { + { 1, 1, 0 }, -- forward along diagonal + { 1, 0, 0 }, -- left + { 0, 1, 0 }, -- right +} + +local north = vector.new(0,0,1) +local south = vector.new(0,0,-1) +local east = vector.new(1,0,0) +local west = vector.new(-1,0,0) + -- Rotate diagonal directions 45 degrees clockwise local diagonal_convert = { - nw = vector.new( 0,0, 1), -- north - ne = vector.new( 1,0, 0), -- east - se = vector.new( 0,0,-1), -- south - sw = vector.new(-1,0, 0), -- west + nw = west, + ne = north, + se = east, + sw = south, } function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) local pos = vector.round(pos_) -- Diagonal conversion + local checks = rail_checks if dir.x ~= 0 and dir.z ~= 0 then dir = diagonal_convert[ mcl_minecarts:name_from_dir(dir, false) ] + checks = rail_checks_diagonal end -- Calculate coordinate space @@ -109,7 +122,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) local up = vector.new(0,1,0) -- Perform checks - for _,check in ipairs(rail_checks) do + for _,check in ipairs(checks) do local check_dir = dir * check[1] + right * check[2] + up * check[3] local check_pos = pos + check_dir if mcl_minecarts:is_rail(check_pos,railtype) then From f7360be5e4a4efdc1eac42afdc2bbc4fb2cf6c61 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 12 Mar 2024 07:03:41 +0000 Subject: [PATCH 021/195] Mostly fix carts stopping between powered rails (there is still some strangeness with acceleration physics) --- mods/ENTITIES/mcl_minecarts/init.lua | 22 ++++++++++++++-------- mods/ITEMS/mcl_hoppers/init.lua | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index d5293724b..b3a2aa039 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -122,12 +122,11 @@ local function do_movement_step(self, remaining_distance) local under_pos = pos - vector.new(0,1,0) local under_node_name = minetest.get_node(under_pos).name local under_node_def = minetest.registered_nodes[under_node_name] + local hopper_pulled = false if DEBUG then print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) end if under_node_def and under_node_def.groups.hopper ~= 0 then - if DEBUG then print( "Attempt pull_from_minecart" ) end - if mcl_hoppers.pull_from_minecart( self, under_pos, self._inv_size or 0 ) then - staticdata.delay = 1.5 - end + hopper_pulled = mcl_hoppers.pull_from_minecart( self, under_pos, self._inv_size or 0 ) + if DEBUG then print( "Attempt pull_from_minecart, hopper_pulled="..tostring(hopper_pulled) ) end end -- Get the next direction @@ -137,10 +136,17 @@ local function do_movement_step(self, remaining_distance) end -- Handle end of track - if next_dir == staticdata.dir * -1 then + if next_dir == staticdata.dir * -1 and ( hopper_pulled or next_dir.y == 0 ) then if DEBUG then print("Stopping cart at "..tostring(pos)) end staticdata.velocity = 0 - distence = remaining_distance + + -- If there is a hopper under here, force the cart to stay put for 1.5 seconds + -- to allow redstone time to process + if hopper_pulled then + staticdata.delay = 1.5 + end + + distance = remaining_distance end -- Update cart direction @@ -191,7 +197,7 @@ local function process_acceleration(self, timestep) staticdata.velocity = ( staticdata.velocity or 0 ) + acceleration * timestep if staticdata.velocity > max_vel then staticdata.velocity = max_vel - elseif staticdata.velocity < 0.1 then + elseif staticdata.velocity < (friction/5) then staticdata.velocity = 0 end end @@ -265,7 +271,7 @@ local function do_movement( self, dtime ) process_acceleration(self,dtime * max_step_distance / total_distance) -- Skip processing stopped railcarts - if not staticdata.velocity or math.abs(staticdata.velocity) < 0.05 then + if not staticdata.velocity or math.abs(staticdata.velocity) < (friction/5) then return end diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index 9af8ff650..31a0bc49b 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -448,7 +448,7 @@ local function hopper_pull_from_mc(mc_ent, dest_pos, inv_size) local dest_inv = dest_meta:get_inventory() if not dest_inv then mcl_log("No dest inv") - return + return false end mcl_log("inv. size: " .. mc_ent._inv_size) From 4a53a515b107fc839ba058fa7adb6c10cb2c9700 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 13 Mar 2024 10:28:40 +0000 Subject: [PATCH 022/195] Move code that handles below-rail hoppers to handle_cart_enter, implement timestep-independent cart physics (will need tuning punch, power rail and gravityaccelerations to make game fun) --- mods/ENTITIES/mcl_minecarts/init.lua | 380 +++++++++++++++++---------- 1 file changed, 237 insertions(+), 143 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index b3a2aa039..8732e9e8a 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -21,7 +21,8 @@ local function mcl_log(message) end end -local function handle_cart_enter(self,pos) +local function handle_cart_enter(self, pos, next_dir) + local staticdata = self._staticdata local node = minetest.get_node(pos) -- Handle track behaviors @@ -30,11 +31,35 @@ local function handle_cart_enter(self,pos) node_def._mcl_minecarts_on_enter(pos, cart) end + -- check for hopper under the rail + local under_pos = pos - vector.new(0,1,0) + local under_node_name = minetest.get_node(under_pos).name + local under_node_def = minetest.registered_nodes[under_node_name] + if under_node_def._mcl_minecarts_on_enter_above then + under_node_def._mcl_minecarts_on_enter_above(under_pos, cart) + else + local hopper_pulled = false + if DEBUG then print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) end + if under_node_def and under_node_def.groups.hopper ~= 0 then + hopper_pulled = mcl_hoppers.pull_from_minecart( self, under_pos, self._inv_size or 0 ) + if DEBUG then print( "Attempt pull_from_minecart, hopper_pulled="..tostring(hopper_pulled) ) end + + if hopper_pulled and next_dir ~= staticdata.dir then + -- If there was an item pulled by a hopper under the rails force the cart to stay put for 1.5 seconds + -- to allow redstone time to process + if hopper_pulled then + staticdata.delay = 1.5 + end + end + end + end + -- Handle above-track behaviors (to ensure hoppers can transfer at least one item) local above_pos = pos + vector.new(0,1,0) - local node = minetest.get_node(above_pos) - if node_def._mcl_minecarts_on_enter_below then - node_def._mcl_minecarts_on_enter_below(above_pos, cart) + local above_node_name = minetest.get_node(above_pos).name + local above_node_def = minetest.registered_nodes[above_node_name] + if above_node_def._mcl_minecarts_on_enter_below then + above_node_def._mcl_minecarts_on_enter_below(above_pos, cart) end -- Handle cart-specific behaviors @@ -90,88 +115,12 @@ local function update_cart_orientation(self,staticdata) self.object:set_rotation(rot) end -local function do_movement_step(self, remaining_distance) - local staticdata = self._staticdata - - local pos = staticdata.connected_at - - if not pos then return remaining_distance end - if staticdata.velocity < 0.1 then return remaining_distance end - - local remaining_in_block = 1 - ( staticdata.distance or 0 ) - local dinstance = 0 - if remaining_in_block > remaining_distance then - distance = remaining_distance - staticdata.distance = ( staticdata.distance or 0 ) + distance - pos = pos + staticdata.dir * staticdata.distance - else - distance = remaining_in_block - staticdata.distance = 0 - - -- Leave the old node - handle_cart_leave(pos) - - -- Anchor at the next node - pos = pos + staticdata.dir - staticdata.connected_at = pos - - -- Enter the new node - handle_cart_enter(self, pos) - - -- check for hopper under the rail - local under_pos = pos - vector.new(0,1,0) - local under_node_name = minetest.get_node(under_pos).name - local under_node_def = minetest.registered_nodes[under_node_name] - local hopper_pulled = false - if DEBUG then print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) end - if under_node_def and under_node_def.groups.hopper ~= 0 then - hopper_pulled = mcl_hoppers.pull_from_minecart( self, under_pos, self._inv_size or 0 ) - if DEBUG then print( "Attempt pull_from_minecart, hopper_pulled="..tostring(hopper_pulled) ) end - end - - -- Get the next direction - local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) - if DEBUG and next_dir ~= staticdata.dir then - print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) - end - - -- Handle end of track - if next_dir == staticdata.dir * -1 and ( hopper_pulled or next_dir.y == 0 ) then - if DEBUG then print("Stopping cart at "..tostring(pos)) end - staticdata.velocity = 0 - - -- If there is a hopper under here, force the cart to stay put for 1.5 seconds - -- to allow redstone time to process - if hopper_pulled then - staticdata.delay = 1.5 - end - - distance = remaining_distance - end - - -- Update cart direction - staticdata.dir = next_dir - end - - self.object:move_to(pos) - - -- Update cart orientation - update_cart_orientation(self,staticdata) - - -- Report distance traveled - return distance -end - -local function process_acceleration(self, timestep) - local staticdata = self._staticdata - if not staticdata.connected_at then return end - +local function calculate_acceleration(self, staticdata) local acceleration = 0 - if DEBUG and staticdata.velocity > 0 then - print( " acceleration="..tostring(acceleration)..",velocity="..tostring(staticdata.velocity).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir)) + -- Apply friction if moving + if staticdata.velocity > 0 then + acceleration = -friction end local pos = staticdata.connected_at @@ -188,64 +137,218 @@ local function process_acceleration(self, timestep) elseif self._fueltime and self._fueltime > 0 then acceleration = 0.6 elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then - acceleration = 0 + -- Standard friction else - acceleration = node_def._rail_acceleration or -friction + acceleration = node_def._rail_acceleration or acceleration end - if math.abs(acceleration) > (friction / 5) then + -- Factor in gravity after everything else + local gravity_strength = 9.8 --friction * 5 + if staticdata.dir.y < 0 then + acceleration = gravity_strength - friction + elseif staticdata.dir.y > 0 then + acceleration = -gravity_strength + friction + end + + return acceleration +end + +local function do_movement_step(self, dtime) + local staticdata = self._staticdata + if not staticdata.connected_at then return 0 end + + -- Calculate timestep remaiing in this block + local x_0 = staticdata.distance or 0 + local remaining_in_block = 1 - x_0 + local a = calculate_acceleration(self, staticdata) + local v_0 = staticdata.velocity + + if DEBUG and ( v_0 > 0 or a ~= 0 ) then + print( " cart #"..tostring(staticdata.cart_id).. + ": a="..tostring(a).. + ",v_0="..tostring(v_0).. + ",x_0="..tostring(x_0).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir).. + ",connected_at="..tostring(staticdata.connected_at).. + ",distance="..tostring(staticdata.distance) + ) + end + + -- Not moving + if a == 0 and v_0 == 0 then return 0 end + + -- Movement equation with acceleration: x_1 = x_0 + v_0 * t + 0.5 * a * t*t + local timestep + local stops_in_block = false + local inside = v_0 * v_0 + 2 * a * remaining_in_block + if inside < 0 then + -- Would stop or reverse direction inside this block, calculate time to v_1 = 0 + timestep = -v_0 / a + stops_in_block = true + elseif a ~= 0 then + -- Setting x_1 = x_0 + remaining_in_block, and solving for t gives: + timestep = ( math.sqrt( v_0 * v_0 + 2 * a * remaining_in_block) - v_0 ) / a + else + timestep = remaining_in_block / v_0 + end + + -- Truncate timestep to remaining time delta + if timestep > dtime then + timestep = dtime + end + + -- Truncate timestep to prevent v_1 from being larger that speed_max + local v_max = mcl_minecarts.speed_max + if v_0 + a * timestep > v_max then + timestep = ( v_max - v_0 ) / a + end + + -- Prevent infinite loops + if timestep <= 0 then return 0 end + + -- Calculate v_1 taking v_max into account + local v_1 = v_0 + a * timestep + if v_1 > v_max then + v_1 = v_max + elseif v_1 < friction / 5 then + v_1 = 0 + end + + -- Calculate x_1 + local x_1 = x_0 + timestep * v_0 + 0.5 * a * timestep * timestep + + -- Update position and velocity of the minecart + staticdata.velocity = v_1 + staticdata.distance = x_1 + + -- Entity movement + local pos = staticdata.connected_at + + -- Handle movement to next block, account for loss of precision in calculations + if x_1 >= 0.99 then + staticdata.distance = 0 + + -- Leave the old node + handle_cart_leave(pos) + + -- Anchor at the next node + pos = pos + staticdata.dir + staticdata.connected_at = pos + + -- Get the next direction + local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) + if DEBUG and next_dir ~= staticdata.dir then + print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) + end + + -- Enter the new node + handle_cart_enter(self, pos, next_dir) + + -- Handle end of track + if next_dir == staticdata.dir * -1 and ( hopper_pulled or next_dir.y == 0 ) then + if DEBUG then print("Stopping cart at end of track at "..tostring(pos)) end + staticdata.velocity = 0 + end + + -- Update cart direction + staticdata.dir = next_dir + elseif stops_in_block and v_1 < (friction/5) and a <= 0 then + -- Handle direction flip due to gravity + if DEBUG then print("Gravity flipped direction") end + + -- Velocity should be zero at this point + staticdata.velocity = 0 + + -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at + local next_dir = -staticdata.dir + pos = pos + staticdata.dir + staticdata.distance = 1 - staticdata.distance + staticdata.connected_at = pos + + -- recalculate direction + local next_dir,_ = mcl_minecarts:get_rail_direction(pos + staticdata.dir, next_dir, nil, nil, staticdata.railtype) + staticdata.dir = next_dir + + -- Intermediate movement + pos = pos + staticdata.dir * staticdata.distance + else + -- Intermediate movement + pos = pos + staticdata.dir * staticdata.distance + end + + self.object:move_to(pos) + + -- Update cart orientation + update_cart_orientation(self,staticdata) + + -- Debug reporting + if DEBUG and ( v_0 > 0 or v_1 > 0 ) then + print( " cart #"..tostring(staticdata.cart_id).. + ": a="..tostring(a).. + ",v_0="..tostring(v_0).. + ",v_1="..tostring(v_1).. + ",x_0="..tostring(x_0).. + ",x_1="..tostring(x_1).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir).. + ",pos="..tostring(pos).. + ",connected_at="..tostring(staticdata.connected_at).. + ",distance="..tostring(staticdata.distance) + ) + end + + -- Report the amount of time processed + return dtime - timestep + + --[[ + + ---Process acceleration + local pos = staticdata.connected_at + + -- Apply acceleleration + if math.abs(acceleration) > 0 then + -- Apply simple acceleration staticdata.velocity = ( staticdata.velocity or 0 ) + acceleration * timestep + + -- Apply friction to resist direction of velocity + if staticdata.velocity > 0 then + staticdata.velocity = ( staticdata.velocity ) - friction * timestep + + -- Velocity flip due to friction should stop movement + if staticdata.velocity < 0 then + if DEBUG then print("Friction stopped minecart") end + staticdata.velocity = 0 + end + else + end + if staticdata.velocity > max_vel then staticdata.velocity = max_vel elseif staticdata.velocity < (friction/5) then + if DEBUG then print("Stopping cart at "..tostring(pos)) end staticdata.velocity = 0 end end - -- Factor in gravity after everything else - local gravity_accel = 0 - local gravity_strength = friction + 0.2 - if staticdata.dir.y < 0 then - gravity_accel = gravity_strength - elseif staticdata.dir.y > 0 then - gravity_accel = -gravity_strength - end - if DEBUG and gravity_accel ~= 0 then - print("gravity_accel="..tostring(gravity_accel)) - end - if gravity_accel ~= 0 then - staticdata.velocity = (staticdata.velocity or 0) + gravity_accel - if staticdata.velocity < 0 then - if DEBUG then - print("Gravity flipped direction") - end - - -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at - staticdata.velocity = staticdata.velocity * -1 - next_dir = -staticdata.dir - pos = pos + staticdata.dir - staticdata.distance = 1 - staticdata.distance - staticdata.connected_at = pos - - local next_dir,_ = mcl_minecarts:get_rail_direction(pos + staticdata.dir, next_dir, nil, nil, staticdata.railtype) - staticdata.dir = next_dir - - update_cart_orientation(self,staticdata) - end - end - -- Force the cart to stop if moving slowly enough - if (staticdata.velocity or 0) < 0.1 then + if (staticdata.velocity or 0) < (friction/5) then staticdata.velocity = 0 end - if DEBUG and staticdata.velocity > 0 then - print( " acceleration="..tostring(acceleration)..",velocity="..tostring(staticdata.velocity).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir)) - end + + -- Handle movement + local pos = staticdata.connected_at + + if not pos then return remaining_distance end + if staticdata.velocity < 0.1 then return remaining_distance end + + -- Report distance traveled + return distance + ]]-- end + local function do_movement( self, dtime ) local staticdata = self._staticdata @@ -262,25 +365,11 @@ local function do_movement( self, dtime ) staticdata.velocity = initial_velocity end - -- Break long movements into fixed-size steps so that - -- it is impossible to jump across gaps due to server lag + -- Break long movements at block boundaries to make it + -- it impossible to jump across gaps due to server lag -- causing large timesteps - local total_distance = dtime * ( staticdata.velocity or 0 ) - local remaining_distance = total_distance - - process_acceleration(self,dtime * max_step_distance / total_distance) - - -- Skip processing stopped railcarts - if not staticdata.velocity or math.abs(staticdata.velocity) < (friction/5) then - return - end - - while remaining_distance > 0.1 do - local step_distance = do_movement_step(self, remaining_distance) - if step_distance > 0.1 then - process_acceleration(self, dtime * step_distance / total_distance) - end - remaining_distance = remaining_distance - step_distance + while dtime > 0 do + dtime = do_movement_step(self, dtime) end -- Clear punched flag now that movement for this step has been completed @@ -457,6 +546,7 @@ local function make_staticdata( railtype, connected_at, dir ) distance = 0, velocity = 0, dir = vector.new(dir), + cart_id = math.random(1,1000000000), } end @@ -641,6 +731,10 @@ local function register_entity(entity_id, def) function cart:on_step(dtime) hopper_take_item(self, dtime) local staticdata = self._staticdata + + -- Make sure all carts have an ID to isolate them + staticdata.cart_id = staticdata.cart_id or math.random(1,1000000000) + if not staticdata then staticdata = make_staticdata() self._staticdata = staticdata From d8bfa03a326d32bec113803c351a4303e8200531 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 13 Mar 2024 11:51:39 +0000 Subject: [PATCH 023/195] Initial tuning of acceleration/gravity, fix crash when entering an activator rail, detach mobs from cart on active activator rail, remove commented out code no longer needed --- mods/ENTITIES/mcl_minecarts/init.lua | 69 +++++++--------------------- 1 file changed, 16 insertions(+), 53 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 8732e9e8a..8dcb403a6 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -28,7 +28,7 @@ local function handle_cart_enter(self, pos, next_dir) -- Handle track behaviors local node_def = minetest.registered_nodes[node.name] if node_def._mcl_minecarts_on_enter then - node_def._mcl_minecarts_on_enter(pos, cart) + node_def._mcl_minecarts_on_enter(pos, self) end -- check for hopper under the rail @@ -36,7 +36,7 @@ local function handle_cart_enter(self, pos, next_dir) local under_node_name = minetest.get_node(under_pos).name local under_node_def = minetest.registered_nodes[under_node_name] if under_node_def._mcl_minecarts_on_enter_above then - under_node_def._mcl_minecarts_on_enter_above(under_pos, cart) + under_node_def._mcl_minecarts_on_enter_above(under_pos, self) else local hopper_pulled = false if DEBUG then print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) end @@ -59,7 +59,7 @@ local function handle_cart_enter(self, pos, next_dir) local above_node_name = minetest.get_node(above_pos).name local above_node_def = minetest.registered_nodes[above_node_name] if above_node_def._mcl_minecarts_on_enter_below then - above_node_def._mcl_minecarts_on_enter_below(above_pos, cart) + above_node_def._mcl_minecarts_on_enter_below(above_pos, self) end -- Handle cart-specific behaviors @@ -139,11 +139,13 @@ local function calculate_acceleration(self, staticdata) elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then -- Standard friction else - acceleration = node_def._rail_acceleration or acceleration + if node_def._rail_acceleration then + acceleration = node_def._rail_acceleration * 4 + end end -- Factor in gravity after everything else - local gravity_strength = 9.8 --friction * 5 + local gravity_strength = 2.45 --friction * 5 if staticdata.dir.y < 0 then acceleration = gravity_strength - friction elseif staticdata.dir.y > 0 then @@ -300,52 +302,6 @@ local function do_movement_step(self, dtime) -- Report the amount of time processed return dtime - timestep - - --[[ - - ---Process acceleration - local pos = staticdata.connected_at - - -- Apply acceleleration - if math.abs(acceleration) > 0 then - -- Apply simple acceleration - staticdata.velocity = ( staticdata.velocity or 0 ) + acceleration * timestep - - -- Apply friction to resist direction of velocity - if staticdata.velocity > 0 then - staticdata.velocity = ( staticdata.velocity ) - friction * timestep - - -- Velocity flip due to friction should stop movement - if staticdata.velocity < 0 then - if DEBUG then print("Friction stopped minecart") end - staticdata.velocity = 0 - end - else - end - - if staticdata.velocity > max_vel then - staticdata.velocity = max_vel - elseif staticdata.velocity < (friction/5) then - if DEBUG then print("Stopping cart at "..tostring(pos)) end - staticdata.velocity = 0 - end - end - - -- Force the cart to stop if moving slowly enough - if (staticdata.velocity or 0) < (friction/5) then - staticdata.velocity = 0 - end - - - -- Handle movement - local pos = staticdata.connected_at - - if not pos then return remaining_distance end - if staticdata.velocity < 0.1 then return remaining_distance end - - -- Report distance traveled - return distance - ]]-- end @@ -360,7 +316,7 @@ local function do_movement( self, dtime ) staticdata.delay = 0 end - local initial_velocity = 1 + local initial_velocity = 2 if self._punched and statcdata.velocity < initial_velocity then staticdata.velocity = initial_velocity end @@ -414,7 +370,14 @@ local function activate_tnt_minecart(self, timer) minetest.sound_play("tnt_ignite", {pos = self.object:get_pos(), gain = 1.0, max_hear_distance = 15}, true) end -local activate_normal_minecart = detach_driver +local function activate_normal_minecart(self) + detach_driver(self) + + if self._passenger then + local mob = self._passenger.object + mob:set_detach() + end +end local function hopper_take_item(self, dtime) local pos = self.object:get_pos() From b8fb8249e983a4b1d377651cd74bf9b154b601ce Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 13 Mar 2024 12:08:12 +0000 Subject: [PATCH 024/195] Increase default track friction, disable right-click to exit minecarts --- mods/ENTITIES/mcl_minecarts/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 8dcb403a6..569c2614c 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -8,7 +8,7 @@ mcl_minecarts.modpath = minetest.get_modpath(modname) mcl_minecarts.speed_max = 10 mcl_minecarts.check_float_time = 15 local max_step_distance = 0.5 -local friction = 0.1 +local friction = 0.4 dofile(mcl_minecarts.modpath.."/functions.lua") dofile(mcl_minecarts.modpath.."/rails.lua") @@ -1021,7 +1021,7 @@ register_minecart({ end local player_name = clicker:get_player_name() if self._driver and player_name == self._driver then - detach_driver(self) + --detach_driver(self) elseif not self._driver then self._driver = player_name self._start_pos = self.object:get_pos() From 390ac27acf75791d19bdc1ecfacf2ead0e3d88ff Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 13 Mar 2024 12:26:15 +0000 Subject: [PATCH 025/195] Prevent players from entering minecarts when sneaking, prevents players from causing https://git.minetest.land/MineClone2/MineClone2/issues/3188 --- mods/ENTITIES/mcl_minecarts/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 569c2614c..0be63ccdd 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -1022,7 +1022,7 @@ register_minecart({ local player_name = clicker:get_player_name() if self._driver and player_name == self._driver then --detach_driver(self) - elseif not self._driver then + elseif not self._driver and not clicker:get_player_control().sneak then self._driver = player_name self._start_pos = self.object:get_pos() mcl_player.player_attached[player_name] = true From f7dcb8f10f75b950ecfef5613a22957cb96fb374 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 13 Mar 2024 13:07:58 +0000 Subject: [PATCH 026/195] Fix forwards/backwars tilt in all directions --- mods/ENTITIES/mcl_minecarts/init.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 0be63ccdd..5ee3462ba 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -111,6 +111,9 @@ local function update_cart_orientation(self,staticdata) if ( staticdata.rot_adjust or 0 ) < 0.01 then rot.x = -rot.x end + if dir.z ~= 0 then + rot.x = -rot.x + end self.object:set_rotation(rot) end From ca2ede87f5e4979e7be3b1dd3533e310e61d07ff Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 13 Mar 2024 14:22:23 +0000 Subject: [PATCH 027/195] Make minecarts solid and add players pushing --- mods/ENTITIES/mcl_minecarts/init.lua | 56 ++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 5ee3462ba..f20e262f2 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -118,6 +118,28 @@ local function update_cart_orientation(self,staticdata) self.object:set_rotation(rot) end +local function direction_away_from_players(self, staticdata) + local objs = minetest.get_objects_inside_radius(self.object:get_pos(), 1.1) + for n=1,#objs do + obj = objs[n] + local player_name = obj:get_player_name() + if player_name and player_name ~= "" and not ( self._driver and self._driver == player_name ) then + local diff = obj:get_pos() - self.object:get_pos() + local length = vector.distance(vector.new(0,0,0),diff) + local vec = diff / length + local force = vector.dot( vec, vector.normalize(staticdata.dir) ) + + if force > 0.5 then + return -length * 4 + elseif force < -0.5 then + return length * 4 + end + end + end + + return 0 +end + local function calculate_acceleration(self, staticdata) local acceleration = 0 @@ -158,6 +180,17 @@ local function calculate_acceleration(self, staticdata) return acceleration end +local function reverse_direction(self, staticdata) + -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at + local next_dir = -staticdata.dir + staticdata.connected_at = staticdata.connected_at + staticdata.dir + staticdata.distance = 1 - staticdata.distance + + -- recalculate direction + local next_dir,_ = mcl_minecarts:get_rail_direction(staticdata.connected_at, next_dir, nil, nil, staticdata.railtype) + staticdata.dir = next_dir +end + local function do_movement_step(self, dtime) local staticdata = self._staticdata if not staticdata.connected_at then return 0 end @@ -168,6 +201,15 @@ local function do_movement_step(self, dtime) local a = calculate_acceleration(self, staticdata) local v_0 = staticdata.velocity + -- Repel minecarts + local away = direction_away_from_players(self, staticdata) + if away > 0 then + v_0 = away + elseif away < 0 then + reverse_direction(self, staticdata) + v_0 = -away + end + if DEBUG and ( v_0 > 0 or a ~= 0 ) then print( " cart #"..tostring(staticdata.cart_id).. ": a="..tostring(a).. @@ -265,18 +307,10 @@ local function do_movement_step(self, dtime) -- Velocity should be zero at this point staticdata.velocity = 0 - -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at - local next_dir = -staticdata.dir - pos = pos + staticdata.dir - staticdata.distance = 1 - staticdata.distance - staticdata.connected_at = pos - - -- recalculate direction - local next_dir,_ = mcl_minecarts:get_rail_direction(pos + staticdata.dir, next_dir, nil, nil, staticdata.railtype) - staticdata.dir = next_dir + reverse_direction(self, staticdata) -- Intermediate movement - pos = pos + staticdata.dir * staticdata.distance + pos = staticdata.connected_at + staticdata.dir * staticdata.distance else -- Intermediate movement pos = pos + staticdata.dir * staticdata.distance @@ -536,7 +570,7 @@ local function register_entity(entity_id, def) assert( def.drop, "drop is required parameter" ) local cart = { initial_properties = { - physical = false, + physical = true, collisionbox = {-10/16., -0.5, -10/16, 10/16, 0.25, 10/16}, visual = "mesh", mesh = def.mesh, From 93373aa1c59333742f69e109addd01805f3ffb6d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 16 Mar 2024 07:59:39 +0000 Subject: [PATCH 028/195] Start adding hooks for implpementing minecart with command block --- mods/ENTITIES/mcl_minecarts/init.lua | 18 ++++++++++++++++-- .../REDSTONE/mesecons_commandblock/init.lua | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index f20e262f2..22b3f49f0 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -436,7 +436,6 @@ local function hopper_take_item(self, dtime) local objs = minetest.get_objects_inside_radius(above_pos, 1.25) if objs then - mcl_log("there is an itemstring. Number of objs: ".. #objs) for k, v in pairs(objs) do @@ -581,6 +580,7 @@ local function register_entity(entity_id, def) on_rightclick = def.on_rightclick, on_activate_by_rail = def.on_activate_by_rail, _mcl_minecarts_on_enter = def._mcl_minecarts_on_enter, + _mcl_minecarts_on_place = def._mcl_minecarts_on_place, _driver = nil, -- player who sits in and controls the minecart (only for minecart!) _passenger = nil, -- for mobs @@ -932,6 +932,11 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) local cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0), nil, nil, railtype) cart:set_yaw(minetest.dir_to_yaw(cart_dir)) + -- Call placer + if cart._mcl_minecarts_on_place then + cart._mcl_minecarts_on_place(self, placer) + end + -- Update static data local le = cart:get_luaentity() if le then @@ -1176,7 +1181,16 @@ register_minecart({ icon = "mcl_minecarts_minecart_command_block.png", drop = {"mcl_minecarts:minecart"}, on_rightclick = nil, - on_activate_by_rail = nil, + _mcl_minecarts_on_place = function(self, placer) + if mesecon and mesecon.command_block then + mesecons.command_block.configure_entity(self, placer) + end + end, + on_activate_by_rail = function(self, timer) + if mesecon and mesecond.command_block then + mesecons.command_block.resolve_command_entity(self) + end + end, creative = false }) diff --git a/mods/ITEMS/REDSTONE/mesecons_commandblock/init.lua b/mods/ITEMS/REDSTONE/mesecons_commandblock/init.lua index 3902c3c18..ec41785df 100644 --- a/mods/ITEMS/REDSTONE/mesecons_commandblock/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_commandblock/init.lua @@ -3,6 +3,8 @@ local F = minetest.formspec_escape local tonumber = tonumber +--mesecon.command_block = {} + local color_red = mcl_colors.RED local command_blocks_activated = minetest.settings:get_bool("mcl_enable_commandblocks", true) From fba7b04e82016f3ddc3b199365d10f2fe5bf08f4 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 16 Mar 2024 09:17:09 +0000 Subject: [PATCH 029/195] Refactor enter/leave hook processing, add node watches for implementing hopper-to-minecart functionality (should properly handle heavy server lag without missing any time), temporarily disable hopper push/pull to minecart in mcl_hoppers, prepare to move minecart-specific on_step behavior out of main on_step function and to a minecart-specific handler --- mods/ENTITIES/mcl_minecarts/functions.lua | 2 +- mods/ENTITIES/mcl_minecarts/init.lua | 149 ++++++++++++++-------- mods/ITEMS/mcl_hoppers/init.lua | 5 +- 3 files changed, 103 insertions(+), 53 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 48392cbee..3aff98b81 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -68,7 +68,7 @@ end --[[ - An array of (u,v,w) positions to check. Actual direction is u * dir + v * right + w * vector.new(0,1,0) + An array of (u,v,w) positions to check. Actual direction is u * dir + v * right + w * up ]] local rail_checks = { { 1, 0, 0 }, -- forwards diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 22b3f49f0..48ca22215 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -21,59 +21,79 @@ local function mcl_log(message) end end -local function handle_cart_enter(self, pos, next_dir) - local staticdata = self._staticdata - local node = minetest.get_node(pos) +mcl_minecarts.on_enter_below = function(pos, cart, next_dir, node_def) + local staticdata = cart._staticdata + if (node_def.groups.hopper or 0) == 0 then return end - -- Handle track behaviors - local node_def = minetest.registered_nodes[node.name] - if node_def._mcl_minecarts_on_enter then - node_def._mcl_minecarts_on_enter(pos, self) - end + local hopper_pulled = mcl_hoppers.pull_from_minecart( cart, pos, cart._inv_size or 0 ) + if DEBUG then print( "Attempt pull_from_minecart, hopper_pulled="..tostring(hopper_pulled) ) end - -- check for hopper under the rail - local under_pos = pos - vector.new(0,1,0) - local under_node_name = minetest.get_node(under_pos).name - local under_node_def = minetest.registered_nodes[under_node_name] - if under_node_def._mcl_minecarts_on_enter_above then - under_node_def._mcl_minecarts_on_enter_above(under_pos, self) - else - local hopper_pulled = false - if DEBUG then print( "under_node_name="..under_node_name..", hopper="..tostring(under_node_def.groups.hopper)) end - if under_node_def and under_node_def.groups.hopper ~= 0 then - hopper_pulled = mcl_hoppers.pull_from_minecart( self, under_pos, self._inv_size or 0 ) - if DEBUG then print( "Attempt pull_from_minecart, hopper_pulled="..tostring(hopper_pulled) ) end - - if hopper_pulled and next_dir ~= staticdata.dir then - -- If there was an item pulled by a hopper under the rails force the cart to stay put for 1.5 seconds - -- to allow redstone time to process - if hopper_pulled then - staticdata.delay = 1.5 - end - end + if hopper_pulled and next_dir ~= staticdata.dir then + -- If there was an item pulled by a hopper under the rails force the cart to stay put for 1.5 seconds + -- to allow redstone time to process + if hopper_pulled then + staticdata.delay = 1.5 end end - - -- Handle above-track behaviors (to ensure hoppers can transfer at least one item) - local above_pos = pos + vector.new(0,1,0) - local above_node_name = minetest.get_node(above_pos).name - local above_node_def = minetest.registered_nodes[above_node_name] - if above_node_def._mcl_minecarts_on_enter_below then - above_node_def._mcl_minecarts_on_enter_below(above_pos, self) - end - - -- Handle cart-specific behaviors - if self._mcl_minecarts_on_enter then - self._mcl_minecarts_on_enter(self, pos) - end end -local function handle_cart_leave(pos) - local node_name = minetest.get_node(pos).name - local node_def = minetest.registered_nodes[node_name] - if node_def._mcl_minecarts_on_leave then - node_def._mcl_minecarts_on_leave(pos, self) +--[[ + Array of hooks { {u,v,w}, name } + Actual position is pos + u * dir + v * right + w * up +]] +local enter_exit_checks = { + { 0, 0, 0, "" }, + { 0, 0, 1, "_above" }, + { 0, 0,-1, "_below" }, + { 0, 1, 0, "_side" }, + { 0,-1, 0, "_side" }, +} + +local function handle_cart_enter_exit(self, pos, next_dir, event) + local staticdata = self._staticdata + + local dir = staticdata.dir + local right = vector.new( dir.z, dir.y, -dir.x) + local up = vector.new(0,1,0) + for _,check in ipairs(enter_exit_checks) do + local check_pos = pos + dir * check[1] + right * check[2] + up * check[3] + local node = minetest.get_node(check_pos) + local node_def = minetest.registered_nodes[node.name] + + -- node-specific hook + local hook = node_def["_mcl_minecarts_"..event..check[4]] + if hook then hook(check_pos, self, next_dir) end + + -- global minecart hook + hook = mcl_minecarts[event..check[4]] + if hook then hook(check_pos, self, next_dir, node_def) end end + + -- Handle cart-specific behaviors + local hook = self["_mcl_minecarts_"..event] + if hook then hook(self, pos) end +end +local function handle_cart_enter(self, pos, next_dir) + handle_cart_enter_exit(self, pos, next_dir, "on_enter" ) +end +local function handle_cart_leave(self, pos, next_dir) + handle_cart_enter_exit(self, pos, next_dir, "on_leave" ) +end + +local function handle_cart_node_watches(self, dtime) + local staticdata = self._staticdata + local watches = staticdata.node_watches or {} + local new_watches = {} + for _,node_pos in ipairs(watches) do + local node = minetest.get_node(node_pos) + local node_def = minetest.registered_nodes[node.name] + local hook = node_def._mcl_minecarts_node_on_step + if hook and hook(node_pos, self, dtime) then + new_watches[#new_watches] = node_pos + end + end + + staticdata.node_watches = new_watches end local function update_cart_orientation(self,staticdata) @@ -276,10 +296,8 @@ local function do_movement_step(self, dtime) if x_1 >= 0.99 then staticdata.distance = 0 - -- Leave the old node - handle_cart_leave(pos) - -- Anchor at the next node + local old_pos = pos pos = pos + staticdata.dir staticdata.connected_at = pos @@ -289,6 +307,9 @@ local function do_movement_step(self, dtime) print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) end + -- Leave the old node + handle_cart_leave(self, old_pos, next_dir ) + -- Enter the new node handle_cart_enter(self, pos, next_dir) @@ -362,7 +383,12 @@ local function do_movement( self, dtime ) -- it impossible to jump across gaps due to server lag -- causing large timesteps while dtime > 0 do - dtime = do_movement_step(self, dtime) + local new_dtime = do_movement_step(self, dtime) + + -- Handle node watches here in steps to prevent server lag from changing behavior + handle_cart_node_watches(self, dtime - new_dtime) + + dtime = new_dtime end -- Clear punched flag now that movement for this step has been completed @@ -581,6 +607,7 @@ local function register_entity(entity_id, def) on_activate_by_rail = def.on_activate_by_rail, _mcl_minecarts_on_enter = def._mcl_minecarts_on_enter, _mcl_minecarts_on_place = def._mcl_minecarts_on_place, + _mcl_minecarts_on_step = def._mcl_minecarts_on_step, _driver = nil, -- player who sits in and controls the minecart (only for minecart!) _passenger = nil, -- for mobs @@ -728,8 +755,20 @@ local function register_entity(entity_id, def) local passenger_attach_position = vector.new(0, -1.75, 0) + function cart:add_node_watch(pos) + local staticdata = self._staticdata + local watches = staticdata.watches + for _,watch in ipairs(watches) do + if watch == pos then return end + end + + watches[#watches+1] = pos + end + function cart:on_step(dtime) + -- TODO: move to _mcl_minecarts_on_step handler and on_enter handler for hopper minecart hopper_take_item(self, dtime) + local staticdata = self._staticdata -- Make sure all carts have an ID to isolate them @@ -787,6 +826,7 @@ local function register_entity(entity_id, def) end end + -- TODO: move to _mcl_minecarts_on_step handler for plain minecart -- Grab mob if math.random(1,20) > 15 and not self._passenger then if self.name == "mcl_minecarts:minecart" then @@ -811,6 +851,7 @@ local function register_entity(entity_id, def) end -- Drop minecart if it isn't on a rail anymore + --[[ Remove this entirely once off-cart minecart behavior is implemented if self._last_float_check == nil then self._last_float_check = 0 else @@ -837,7 +878,14 @@ local function register_entity(entity_id, def) end self._last_float_check = 0 end + ]] + local hook = cart._mcl_minecarts_on_step + if hook then hook(cart,dtime) end + + -- TODO: move the below into cart-specific hooks + + -- TODO: move to _mcl_minecarts_on_step handler for furnace minecart -- Update furnace stuff if self._fueltime and self._fueltime > 0 then self._fueltime = self._fueltime - dtime @@ -857,6 +905,7 @@ local function register_entity(entity_id, def) end local has_fuel = self._fueltime and self._fueltime > 0 + -- TODO: move to _mcl_minecarts_on_step handler for TNT minecart -- Update TNT stuff if self._boomtimer then -- Explode diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index 31a0bc49b..07971c42f 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -437,6 +437,7 @@ minetest.register_node("mcl_hoppers:hopper_side_disabled", def_hopper_side_disab --[[ END OF NODE DEFINITIONS ]] + local function hopper_pull_from_mc(mc_ent, dest_pos, inv_size) local inv = mcl_entity_invs.load_inv(mc_ent, inv_size) if not inv then @@ -558,7 +559,7 @@ minetest.register_abm({ and (hm_pos.z >= pos.z - DIST_FROM_MC and hm_pos.z <= pos.z + DIST_FROM_MC) then mcl_log("Minecart close enough") if entity.name == "mcl_minecarts:hopper_minecart" then - hopper_pull_from_mc(entity, pos, 5) + --hopper_pull_from_mc(entity, pos, 5) elseif entity.name == "mcl_minecarts:chest_minecart" or entity.name == "mcl_boats:chest_boat" then hopper_pull_from_mc(entity, pos, 27) end @@ -567,7 +568,7 @@ minetest.register_abm({ and (hm_pos.z >= pos.z - DIST_FROM_MC and hm_pos.z <= pos.z + DIST_FROM_MC) then mcl_log("Minecart close enough") if entity.name == "mcl_minecarts:hopper_minecart" then - hopper_push_to_mc(entity, pos, 5) + --hopper_push_to_mc(entity, pos, 5) elseif entity.name == "mcl_minecarts:chest_minecart" or entity.name == "mcl_boats:chest_boat" then hopper_push_to_mc(entity, pos, 27) end From 615df7378a429236fcdd5c64304e86c6cb40b2b6 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 16 Mar 2024 09:21:39 +0000 Subject: [PATCH 030/195] Add API function to remove node watch --- mods/ENTITIES/mcl_minecarts/init.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 48ca22215..bf51ba593 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -764,6 +764,15 @@ local function register_entity(entity_id, def) watches[#watches+1] = pos end + function cart:remove_node_watch(pos) + local new_watches = {} + for _,node_pos in ipairs(watches) do + if node_pos ~= post then + new_watches[#new_watches] = node_pos + end + end + staticdata.node_watches = new_watches + end function cart:on_step(dtime) -- TODO: move to _mcl_minecarts_on_step handler and on_enter handler for hopper minecart From f5616738e73f51cd88994278358809f88c490f0e Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 23 Mar 2024 06:13:53 +0000 Subject: [PATCH 031/195] Add groups to minecart entities (for containers), fix cart node watch handling, relocate hopper_push_to_mc in mcl_hopper/init.lua, implement hopper-to-minecart push using enter/leave hooks for both straight and bent hoppers --- mods/ENTITIES/mcl_minecarts/init.lua | 36 +++++-- mods/ITEMS/mcl_hoppers/init.lua | 144 +++++++++++++++++++-------- 2 files changed, 129 insertions(+), 51 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index bf51ba593..31a648c95 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -61,8 +61,9 @@ local function handle_cart_enter_exit(self, pos, next_dir, event) local node_def = minetest.registered_nodes[node.name] -- node-specific hook - local hook = node_def["_mcl_minecarts_"..event..check[4]] - if hook then hook(check_pos, self, next_dir) end + local hook_name = "_mcl_minecarts_"..event..check[4] + local hook = node_def[hook_name] + if hook then hook(check_pos, self, next_dir, pos) end -- global minecart hook hook = mcl_minecarts[event..check[4]] @@ -89,7 +90,7 @@ local function handle_cart_node_watches(self, dtime) local node_def = minetest.registered_nodes[node.name] local hook = node_def._mcl_minecarts_node_on_step if hook and hook(node_pos, self, dtime) then - new_watches[#new_watches] = node_pos + new_watches[#new_watches+1] = node_pos end end @@ -141,7 +142,7 @@ end local function direction_away_from_players(self, staticdata) local objs = minetest.get_objects_inside_radius(self.object:get_pos(), 1.1) for n=1,#objs do - obj = objs[n] + local obj = objs[n] local player_name = obj:get_player_name() if player_name and player_name ~= "" and not ( self._driver and self._driver == player_name ) then local diff = obj:get_pos() - self.object:get_pos() @@ -314,7 +315,7 @@ local function do_movement_step(self, dtime) handle_cart_enter(self, pos, next_dir) -- Handle end of track - if next_dir == staticdata.dir * -1 and ( hopper_pulled or next_dir.y == 0 ) then + if next_dir == staticdata.dir * -1 and next_dir.y == 0 then if DEBUG then print("Stopping cart at end of track at "..tostring(pos)) end staticdata.velocity = 0 end @@ -593,6 +594,13 @@ end local function register_entity(entity_id, def) assert( def.drop, "drop is required parameter" ) + + -- Entity groups + local groups = { minecart = 1 } + for k,v in pairs(def.groups or {}) do + groups[k] = v + end + local cart = { initial_properties = { physical = true, @@ -603,8 +611,11 @@ local function register_entity(entity_id, def) textures = def.textures, }, + groups = groups, + on_rightclick = def.on_rightclick, on_activate_by_rail = def.on_activate_by_rail, + _mcl_minecarts_on_enter = def._mcl_minecarts_on_enter, _mcl_minecarts_on_place = def._mcl_minecarts_on_place, _mcl_minecarts_on_step = def._mcl_minecarts_on_step, @@ -665,7 +676,7 @@ local function register_entity(entity_id, def) staticdata.connected_at = rounded_pos pos = rounded_pos else - print("TODO: handle detached cart behavior") + mineclone.log("warning","TODO: handle detached cart behavior") end end @@ -757,14 +768,19 @@ local function register_entity(entity_id, def) function cart:add_node_watch(pos) local staticdata = self._staticdata - local watches = staticdata.watches + local watches = staticdata.node_watches or {} + for _,watch in ipairs(watches) do if watch == pos then return end end watches[#watches+1] = pos + staticdata.node_watches = watches end function cart:remove_node_watch(pos) + local staticdata = self._staticdata + local watches = staticdata.node_watches or {} + local new_watches = {} for _,node_pos in ipairs(watches) do if node_pos ~= post then @@ -792,8 +808,6 @@ local function register_entity(entity_id, def) end local pos, rou_pos, node = self.object:get_pos() - --local update = {} - --local acceleration = 0 -- Controls local ctrl, player = nil, nil @@ -820,7 +834,7 @@ local function register_entity(entity_id, def) do_movement(self, dtime) - -- TODO: move this into do_movement_step + -- TODO: move this into mcl_core:cactus _mcl_minecarts_on_enter_side -- Drop minecart if it collides with a cactus node local r = 0.6 for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do @@ -1156,6 +1170,7 @@ register_minecart({ }, icon = "mcl_minecarts_minecart_chest.png", drop = {"mcl_minecarts:minecart", "mcl_chests:chest"}, + groups = { container = 1 }, on_rightclick = nil, on_activate_by_rail = nil, creative = true @@ -1269,6 +1284,7 @@ register_minecart({ }, icon = "mcl_minecarts_minecart_hopper.png", drop = {"mcl_minecarts:minecart", "mcl_hoppers:hopper"}, + groups = { container = 1 }, on_rightclick = nil, on_activate_by_rail = nil, _mcl_minecarts_on_enter = function(self,pos) diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index 07971c42f..97f99fd37 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -96,6 +96,48 @@ local function bent_hopper_act(pos, node, active_object_count, active_object_cou mcl_util.hopper_pull(pos, src_pos) end +local function hopper_push_to_mc(mc_ent, dest_pos, inv_size) + local dest_inv = mcl_entity_invs.load_inv(mc_ent, inv_size) + if not dest_inv then + mcl_log("No inv") + return false + end + + local meta = minetest.get_meta(dest_pos) + local inv = meta:get_inventory() + if not inv then + mcl_log("No dest inv") + return + end + + mcl_log("inv. size: " .. mc_ent._inv_size) + for i = 1, mc_ent._inv_size, 1 do + local stack = inv:get_stack("main", i) + + mcl_log("i: " .. tostring(i)) + mcl_log("Name: [" .. tostring(stack:get_name()) .. "]") + mcl_log("Count: " .. tostring(stack:get_count())) + mcl_log("stack max: " .. tostring(stack:get_stack_max())) + + if not stack:get_name() or stack:get_name() ~= "" then + if dest_inv:room_for_item("main", stack:peek_item()) then + mcl_log("Room so unload") + dest_inv:add_item("main", stack:take_item()) + inv:set_stack("main", i, stack) + + -- Take one item and stop until next time + return + else + mcl_log("no Room") + end + + else + mcl_log("nothing there") + end + end +end + + -- Downwards hopper (base definition) ---@type node_definition @@ -201,6 +243,29 @@ local def_hopper = { minetest.log("action", player:get_player_name() .. " takes stuff from mcl_hoppers at " .. minetest.pos_to_string(pos)) end, + _mcl_minecarts_on_enter_above = function(pos, cart, next_dir) + -- Only push to containers + if cart.groups and (cart.groups.container or 0) ~= 0 then + cart:add_node_watch(pos) + end + end, + _mcl_minecarts_on_leave_above = function(pos, cart, next_dir) + cart:remove_node_watch(pos) + end, + _mcl_minecarts_node_on_step = function(pos, cart, dtime) + local meta = minetest.get_meta(pos) + + local timer = meta:get_int("minecart_hopper_timer") + if timer < dtime then + hopper_push_to_mc(cart, pos, 5) + timer = timer + 1 + else + timer = timer - dtime + end + meta:set_int("minecart_hopper_timer", timer) + + return true + end, sounds = mcl_sounds.node_sound_metal_defaults(), _mcl_blast_resistance = 4.8, @@ -406,6 +471,44 @@ local def_hopper_side = { on_rotate = on_rotate, sounds = mcl_sounds.node_sound_metal_defaults(), + _mcl_minecarts_on_enter_side = function(pos, cart, next_dir, rail_pos) + -- Only try to push to minecarts when the spout position is pointed at the rail + local face = minetest.get_node(pos).param2 + local dst_pos = {} + if face == 0 then + dst_pos = vector.offset(pos, -1, 0, 0) + elseif face == 1 then + dst_pos = vector.offset(pos, 0, 0, 1) + elseif face == 2 then + dst_pos = vector.offset(pos, 1, 0, 0) + elseif face == 3 then + dst_pos = vector.offset(pos, 0, 0, -1) + end + if dst_pos ~= rail_pos then return end + + -- Only push to containers + if cart.groups and (cart.groups.container or 0) ~= 0 then + cart:add_node_watch(pos) + end + end, + _mcl_minecarts_on_leave_side = function(pos, cart, next_dir) + cart:remove_node_watch(pos) + end, + _mcl_minecarts_node_on_step = function(pos, cart, dtime) + local meta = minetest.get_meta(pos) + + local timer = meta:get_int("minecart_hopper_timer") + if timer < dtime then + hopper_push_to_mc(cart, pos, 5) + timer = timer + 1 + else + timer = timer - dtime + end + meta:set_int("minecart_hopper_timer", timer) + + return true + end, + _mcl_blast_resistance = 4.8, _mcl_hardness = 3, } @@ -480,47 +583,6 @@ local function hopper_pull_from_mc(mc_ent, dest_pos, inv_size) end mcl_hoppers.pull_from_minecart = hopper_pull_from_mc -local function hopper_push_to_mc(mc_ent, dest_pos, inv_size) - local dest_inv = mcl_entity_invs.load_inv(mc_ent, inv_size) - if not dest_inv then - mcl_log("No inv") - return false - end - - local meta = minetest.get_meta(dest_pos) - local inv = meta:get_inventory() - if not inv then - mcl_log("No dest inv") - return - end - - mcl_log("inv. size: " .. mc_ent._inv_size) - for i = 1, mc_ent._inv_size, 1 do - local stack = inv:get_stack("main", i) - - mcl_log("i: " .. tostring(i)) - mcl_log("Name: [" .. tostring(stack:get_name()) .. "]") - mcl_log("Count: " .. tostring(stack:get_count())) - mcl_log("stack max: " .. tostring(stack:get_stack_max())) - - if not stack:get_name() or stack:get_name() ~= "" then - if dest_inv:room_for_item("main", stack:peek_item()) then - mcl_log("Room so unload") - dest_inv:add_item("main", stack:take_item()) - inv:set_stack("main", i, stack) - - -- Take one item and stop until next time - return - else - mcl_log("no Room") - end - - else - mcl_log("nothing there") - end - end -end - --[[ BEGIN OF ABM DEFINITONS ]] minetest.register_abm({ From 718938b0fe5af49b3fb9ad96d9f981532d1a2b29 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 23 Mar 2024 07:09:20 +0000 Subject: [PATCH 032/195] Fix call signature of mcl_util.hopper_pull_to_inventory, move cart-specific behaviors to _mcl_minecarts_on_step handlers, fix typo, change distance used in rail reattach code, move cart_id generation --- mods/CORE/mcl_util/init.lua | 2 +- mods/ENTITIES/mcl_minecarts/init.lua | 246 ++++++++++++--------------- 2 files changed, 109 insertions(+), 139 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 229c9c92a..d7c2667ce 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -231,7 +231,7 @@ function mcl_util.hopper_push(pos, dst_pos) return ok end -function mcl_util.hopper_pull_to_inventory(hop_inv, hop_list, src_pos) +function mcl_util.hopper_pull_to_inventory(hop_inv, hop_list, src_pos, pos) -- TODO: merge together with hopper_pull after https://git.minetest.land/MineClone2/MineClone2/pulls/4190 is merged -- Get node pos' for item transfer local src = minetest.get_node(src_pos) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 31a648c95..b4639a72c 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -645,6 +645,9 @@ local function register_entity(entity_id, def) -- Fix up types data.dir = vector.new(data.dir) + -- Make sure all carts have an ID to isolate them + data.cart_id = staticdata.cart_id or math.random(1,1000000000) + self._staticdata = data end self.object:set_armor_groups({immortal=1}) @@ -671,12 +674,13 @@ local function register_entity(entity_id, def) pos = self.object:get_pos() -- Try to reattach local rounded_pos = vector.round(pos) - if mcl_minecarts:is_rail(rounded_pos) and vector.distance(pos, rounded_pos) < 0.25 then + if mcl_minecarts:is_rail(rounded_pos) and vector.distance(pos, rounded_pos) < 0.5 then -- Reattach staticdata.connected_at = rounded_pos pos = rounded_pos else - mineclone.log("warning","TODO: handle detached cart behavior") + minetest.log("warning","rounded_pos="..tostring(rounded_pos)..",dist="..vector.distance(pos, rounded_pos)) + minetest.log("warning","TODO: handle detached cart behavior") end end @@ -783,7 +787,7 @@ local function register_entity(entity_id, def) local new_watches = {} for _,node_pos in ipairs(watches) do - if node_pos ~= post then + if node_pos ~= pos then new_watches[#new_watches] = node_pos end end @@ -791,18 +795,12 @@ local function register_entity(entity_id, def) end function cart:on_step(dtime) - -- TODO: move to _mcl_minecarts_on_step handler and on_enter handler for hopper minecart - hopper_take_item(self, dtime) - local staticdata = self._staticdata - - -- Make sure all carts have an ID to isolate them - staticdata.cart_id = staticdata.cart_id or math.random(1,1000000000) - if not staticdata then staticdata = make_staticdata() self._staticdata = staticdata end + if (staticdata.hopper_delay or 0) > 0 then staticdata.hopper_delay = staticdata.hopper_delay - dtime end @@ -849,129 +847,9 @@ local function register_entity(entity_id, def) end end - -- TODO: move to _mcl_minecarts_on_step handler for plain minecart - -- Grab mob - if math.random(1,20) > 15 and not self._passenger then - if self.name == "mcl_minecarts:minecart" then - local mobsnear = minetest.get_objects_inside_radius(self.object:get_pos(), 1.3) - for n=1, #mobsnear do - local mob = mobsnear[n] - if mob then - local entity = mob:get_luaentity() - if entity and entity.is_mob then - self._passenger = entity - mob:set_attach(self.object, "", passenger_attach_position, vector.zero()) - break - end - end - end - end - elseif self._passenger then - local passenger_pos = self._passenger.object:get_pos() - if not passenger_pos then - self._passenger = nil - end - end - - -- Drop minecart if it isn't on a rail anymore - --[[ Remove this entirely once off-cart minecart behavior is implemented - if self._last_float_check == nil then - self._last_float_check = 0 - else - self._last_float_check = self._last_float_check + dtime - end - if self._last_float_check >= mcl_minecarts.check_float_time then - pos = self.object:get_pos() - rou_pos = vector.round(pos) - node = minetest.get_node(rou_pos) - local g = minetest.get_item_group(node.name, "connect_to_raillike") - if g ~= self._staticdata.railtype and self._staticdata.railtype then - -- Detach driver - detach_driver(self) - - -- Explode if already ignited - if self._boomtimer then - self.object:remove() - mcl_explosions.explode(pos, 4, { drop_chance = 1.0 }) - return - end - - -- Do not drop minecart. It goes off the rails too frequently, and anyone using them for farms won't - -- notice and lose their iron and not bother. Not cool until fixed. - end - self._last_float_check = 0 - end - ]] - - local hook = cart._mcl_minecarts_on_step - if hook then hook(cart,dtime) end - - -- TODO: move the below into cart-specific hooks - - -- TODO: move to _mcl_minecarts_on_step handler for furnace minecart - -- Update furnace stuff - if self._fueltime and self._fueltime > 0 then - self._fueltime = self._fueltime - dtime - if self._fueltime <= 0 then - self.object:set_properties({textures = - { - "default_furnace_top.png", - "default_furnace_top.png", - "default_furnace_front.png", - "default_furnace_side.png", - "default_furnace_side.png", - "default_furnace_side.png", - "mcl_minecarts_minecart.png", - }}) - self._fueltime = 0 - end - end - local has_fuel = self._fueltime and self._fueltime > 0 - - -- TODO: move to _mcl_minecarts_on_step handler for TNT minecart - -- Update TNT stuff - if self._boomtimer then - -- Explode - self._boomtimer = self._boomtimer - dtime - local pos = self.object:get_pos() - if self._boomtimer <= 0 then - self.object:remove() - mcl_explosions.explode(pos, 4, { drop_chance = 1.0 }) - return - else - tnt.smoke_step(pos) - end - end - if self._blinktimer then - self._blinktimer = self._blinktimer - dtime - if self._blinktimer <= 0 then - self._blink = not self._blink - if self._blink then - self.object:set_properties({textures = - { - "default_tnt_top.png", - "default_tnt_bottom.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "mcl_minecarts_minecart.png", - }}) - else - self.object:set_properties({textures = - { - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_minecarts_minecart.png", - }}) - end - self._blinktimer = tnt.BLINKTIMER - end - end + -- Cart specific behaviors + local hook = self._mcl_minecarts_on_step + if hook then hook(self,dtime) end end function cart:get_staticdata() @@ -1152,7 +1030,31 @@ register_minecart({ end, name) end end, - on_activate_by_rail = activate_normal_minecart + on_activate_by_rail = activate_normal_minecart, + _mcl_minecarts_on_step = function(self, dtime) + -- Grab mob + if math.random(1,20) > 15 and not self._passenger then + if self.name == "mcl_minecarts:minecart" then + local mobsnear = minetest.get_objects_inside_radius(self.object:get_pos(), 1.3) + for n=1, #mobsnear do + local mob = mobsnear[n] + if mob then + local entity = mob:get_luaentity() + if entity and entity.is_mob then + self._passenger = entity + mob:set_attach(self.object, "", passenger_attach_position, vector.zero()) + break + end + end + end + end + elseif self._passenger then + local passenger_pos = self._passenger.object:get_pos() + if not passenger_pos then + self._passenger = nil + end + end + end }) -- Minecart with Chest @@ -1230,7 +1132,27 @@ register_minecart({ end end, on_activate_by_rail = nil, - creative = true + creative = true, + _mcl_minecarts_on_step = function(self, dtime) + -- Update furnace stuff + if self._fueltime and self._fueltime > 0 then + self._fueltime = self._fueltime - dtime + if self._fueltime <= 0 then + self.object:set_properties({textures = + { + "default_furnace_top.png", + "default_furnace_top.png", + "default_furnace_front.png", + "default_furnace_side.png", + "default_furnace_side.png", + "default_furnace_side.png", + "mcl_minecarts_minecart.png", + }}) + self._fueltime = 0 + end + end + --local has_fuel = self._fueltime and self._fueltime > 0 + end }) -- Minecart with Command Block @@ -1287,7 +1209,7 @@ register_minecart({ groups = { container = 1 }, on_rightclick = nil, on_activate_by_rail = nil, - _mcl_minecarts_on_enter = function(self,pos) + _mcl_minecarts_on_enter = function(self, pos) local staticdata = self._staticdata if (staticdata.hopper_delay or 0) > 0 then return @@ -1296,10 +1218,13 @@ register_minecart({ -- try to pull from containers into our inventory local inv = mcl_entity_invs.load_inv(self,5) local above_pos = pos + vector.new(0,1,0) - mcl_util.hopper_pull_to_inventory(inv, 'main', above_pos) + mcl_util.hopper_pull_to_inventory(inv, 'main', above_pos, pos) staticdata.hopper_delay = (staticdata.hopper_delay or 0) + (1/20) end, + _mcl_minecarts_on_step = function(self, dtime) + hopper_take_item(self, dtime) + end, creative = true }) mcl_entity_invs.register_inv("mcl_minecarts:hopper_minecart", "Hopper Minecart", 5, false, true) @@ -1345,7 +1270,52 @@ register_minecart({ end end, on_activate_by_rail = activate_tnt_minecart, - creative = true + creative = true, + _mcl_minecarts_on_step = function(self, dtime) + -- Update TNT stuff + if self._boomtimer then + -- Explode + self._boomtimer = self._boomtimer - dtime + local pos = self.object:get_pos() + if self._boomtimer <= 0 then + self.object:remove() + mcl_explosions.explode(pos, 4, { drop_chance = 1.0 }) + return + else + tnt.smoke_step(pos) + end + end + if self._blinktimer then + self._blinktimer = self._blinktimer - dtime + if self._blinktimer <= 0 then + self._blink = not self._blink + if self._blink then + self.object:set_properties({textures = + { + "default_tnt_top.png", + "default_tnt_bottom.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "mcl_minecarts_minecart.png", + }}) + else + self.object:set_properties({textures = + { + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_minecarts_minecart.png", + }}) + end + self._blinktimer = tnt.BLINKTIMER + end + end + end, }) From bf3872e445e457ee2fa66785b6b160bd7cb07f08 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 23 Mar 2024 07:32:32 +0000 Subject: [PATCH 033/195] Fix bug with furnace minecart at max velocity (stopped until fuel ran out), move _fueltime into staticdata --- mods/ENTITIES/mcl_minecarts/init.lua | 38 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index b4639a72c..c9961230f 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -180,7 +180,7 @@ local function calculate_acceleration(self, staticdata) acceleration = -1.5 elseif self._punched then acceleration = 2 - elseif self._fueltime and self._fueltime > 0 then + elseif (staticdata.fueltime or 0) > 0 then acceleration = 0.6 elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then -- Standard friction @@ -268,7 +268,7 @@ local function do_movement_step(self, dtime) -- Truncate timestep to prevent v_1 from being larger that speed_max local v_max = mcl_minecarts.speed_max - if v_0 + a * timestep > v_max then + if (v_0 ~= v_max) and ( v_0 + a * timestep > v_max) then timestep = ( v_max - v_0 ) / a end @@ -290,6 +290,21 @@ local function do_movement_step(self, dtime) staticdata.velocity = v_1 staticdata.distance = x_1 + if DEBUG and ( v_0 > 0 or a ~= 0 ) then + print( "- cart #"..tostring(staticdata.cart_id).. + ": a="..tostring(a).. + ",v_0="..tostring(v_0).. + ",v_1="..tostring(v_1).. + ",x_0="..tostring(x_0).. + ",x_1="..tostring(x_1).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir).. + ",connected_at="..tostring(staticdata.connected_at).. + ",distance="..tostring(staticdata.distance) + ) + end + + -- Entity movement local pos = staticdata.connected_at @@ -625,7 +640,6 @@ local function register_entity(entity_id, def) _punched = false, -- used to re-send _velocity and position _start_pos = nil, -- Used to calculate distance for “On A Rail” achievement _last_float_check = nil, -- timestamp of last time the cart was checked to be still on a rail - _fueltime = nil, -- how many seconds worth of fuel is left. Only used by minecart with furnace _boomtimer = nil, -- how many seconds are left before exploding _blinktimer = nil, -- how many seconds are left before TNT blinking _blink = false, -- is TNT blink texture active? @@ -1102,16 +1116,15 @@ register_minecart({ icon = "mcl_minecarts_minecart_furnace.png", drop = {"mcl_minecarts:minecart", "mcl_furnaces:furnace"}, on_rightclick = function(self, clicker) + local staticdata = self._staticdata + -- Feed furnace with coal if not clicker or not clicker:is_player() then return end - if not self._fueltime then - self._fueltime = 0 - end local held = clicker:get_wielded_item() if minetest.get_item_group(held:get_name(), "coal") == 1 then - self._fueltime = self._fueltime + 180 + staticdata.fueltime = (staticdata.fueltime or 0) + 180 if not minetest.is_creative_enabled(clicker:get_player_name()) then held:take_item() @@ -1134,10 +1147,12 @@ register_minecart({ on_activate_by_rail = nil, creative = true, _mcl_minecarts_on_step = function(self, dtime) + local staticdata = self._staticdata + -- Update furnace stuff - if self._fueltime and self._fueltime > 0 then - self._fueltime = self._fueltime - dtime - if self._fueltime <= 0 then + if (staticdata.fueltime or 0) > 0 then + staticdata.fueltime = (staticdata.fueltime or dtime) - dtime + if staticdata.fueltime <= 0 then self.object:set_properties({textures = { "default_furnace_top.png", @@ -1148,10 +1163,9 @@ register_minecart({ "default_furnace_side.png", "mcl_minecarts_minecart.png", }}) - self._fueltime = 0 + staticdata.fueltime = 0 end end - --local has_fuel = self._fueltime and self._fueltime > 0 end }) From be241f64fe4fd1133efa0460e1e15a7f11ef73b7 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 23 Mar 2024 08:09:37 +0000 Subject: [PATCH 034/195] Limit top speed of furnace minecarts to 4 blocks/second, limit total fuel time to 27 minutes --- mods/ENTITIES/mcl_minecarts/init.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index c9961230f..b8b7caf39 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -180,7 +180,7 @@ local function calculate_acceleration(self, staticdata) acceleration = -1.5 elseif self._punched then acceleration = 2 - elseif (staticdata.fueltime or 0) > 0 then + elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then acceleration = 0.6 elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then -- Standard friction @@ -815,6 +815,10 @@ local function register_entity(entity_id, def) self._staticdata = staticdata end + -- Cart specific behaviors + local hook = self._mcl_minecarts_on_step + if hook then hook(self,dtime) end + if (staticdata.hopper_delay or 0) > 0 then staticdata.hopper_delay = staticdata.hopper_delay - dtime end @@ -860,10 +864,6 @@ local function register_entity(entity_id, def) return end end - - -- Cart specific behaviors - local hook = self._mcl_minecarts_on_step - if hook then hook(self,dtime) end end function cart:get_staticdata() @@ -1126,6 +1126,11 @@ register_minecart({ if minetest.get_item_group(held:get_name(), "coal") == 1 then staticdata.fueltime = (staticdata.fueltime or 0) + 180 + -- Trucate to 27 minutes (9 uses) + if staticdata.fueltime > 27*60 then + staticdata.fuel_time = 27*60 + end + if not minetest.is_creative_enabled(clicker:get_player_name()) then held:take_item() local index = clicker:get_wield_index() From 3f4d8f5ec28a6023f09eb4c85b6d04391e0dba96 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 23 Mar 2024 09:08:17 +0000 Subject: [PATCH 035/195] Remove cart oscillation when pushed --- mods/ENTITIES/mcl_minecarts/init.lua | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index b8b7caf39..25aa6f415 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -146,14 +146,23 @@ local function direction_away_from_players(self, staticdata) local player_name = obj:get_player_name() if player_name and player_name ~= "" and not ( self._driver and self._driver == player_name ) then local diff = obj:get_pos() - self.object:get_pos() + local length = vector.distance(vector.new(0,0,0),diff) local vec = diff / length local force = vector.dot( vec, vector.normalize(staticdata.dir) ) - if force > 0.5 then - return -length * 4 - elseif force < -0.5 then - return length * 4 + -- Check if this would push past the end of the track and don't move it it would + -- This prevents an oscillation that would otherwise occur + local dir = staticdata.dir + if force > 0 then + dir = -dir + end + if mcl_minecarts:is_rail( staticdata.connected_at + dir ) then + if force > 0.5 then + return -length * 4 + elseif force < -0.5 then + return length * 4 + end end end end @@ -290,7 +299,7 @@ local function do_movement_step(self, dtime) staticdata.velocity = v_1 staticdata.distance = x_1 - if DEBUG and ( v_0 > 0 or a ~= 0 ) then + if DEBUG and (1==0) and ( v_0 > 0 or a ~= 0 ) then print( "- cart #"..tostring(staticdata.cart_id).. ": a="..tostring(a).. ",v_0="..tostring(v_0).. From 3443e327ec456266949bc56032a73c784cb9b928 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 23 Mar 2024 09:59:08 +0000 Subject: [PATCH 036/195] Disable punch to move minecarts, implement punch to drop minecart, enable basic cart keyboard controls (accelerate and brake) --- mods/ENTITIES/mcl_minecarts/init.lua | 160 ++++++++------------------- 1 file changed, 48 insertions(+), 112 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 25aa6f415..f6d2fac02 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -9,6 +9,7 @@ mcl_minecarts.speed_max = 10 mcl_minecarts.check_float_time = 15 local max_step_distance = 0.5 local friction = 0.4 +local MINECART_MAX_HP = 4 dofile(mcl_minecarts.modpath.."/functions.lua") dofile(mcl_minecarts.modpath.."/rails.lua") @@ -184,11 +185,9 @@ local function calculate_acceleration(self, staticdata) local max_vel = mcl_minecarts.speed_max if self._go_forward then - acceleration = 2 + acceleration = 4 elseif self._brake then acceleration = -1.5 - elseif self._punched then - acceleration = 2 elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then acceleration = 0.6 elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then @@ -399,11 +398,6 @@ local function do_movement( self, dtime ) staticdata.delay = 0 end - local initial_velocity = 2 - if self._punched and statcdata.velocity < initial_velocity then - staticdata.velocity = initial_velocity - end - -- Break long movements at block boundaries to make it -- it impossible to jump across gaps due to server lag -- causing large timesteps @@ -415,9 +409,6 @@ local function do_movement( self, dtime ) dtime = new_dtime end - - -- Clear punched flag now that movement for this step has been completed - self._punched = false end local function detach_driver(self) @@ -439,7 +430,6 @@ local function activate_tnt_minecart(self, timer) if self._boomtimer then return end - self.object:set_armor_groups({immortal=1}) if timer then self._boomtimer = timer else @@ -461,6 +451,7 @@ end local function activate_normal_minecart(self) detach_driver(self) + -- Detach passenger if self._passenger then local mob = self._passenger.object mob:set_detach() @@ -635,6 +626,8 @@ local function register_entity(entity_id, def) textures = def.textures, }, + hp_max = MINECART_MAX_HP, + groups = groups, on_rightclick = def.on_rightclick, @@ -646,7 +639,6 @@ local function register_entity(entity_id, def) _driver = nil, -- player who sits in and controls the minecart (only for minecart!) _passenger = nil, -- for mobs - _punched = false, -- used to re-send _velocity and position _start_pos = nil, -- Used to calculate distance for “On A Rail” achievement _last_float_check = nil, -- timestamp of last time the cart was checked to be still on a rail _boomtimer = nil, -- how many seconds are left before exploding @@ -673,7 +665,6 @@ local function register_entity(entity_id, def) self._staticdata = data end - self.object:set_armor_groups({immortal=1}) -- Activate cart if on activator rail if self.on_activate_by_rail then @@ -684,110 +675,36 @@ local function register_entity(entity_id, def) end end end - - function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) + function cart:on_death(killer) local staticdata = self._staticdata - if not staticdata then - staticdata = make_staticdata() - self._staticdata = staticdata + detach_driver(self) + + -- Detach passenger + if self._passenger then + local mob = self._passenger.object + mob:set_detach() end - local pos = staticdata.connected_at - if not pos then - pos = self.object:get_pos() - -- Try to reattach - local rounded_pos = vector.round(pos) - if mcl_minecarts:is_rail(rounded_pos) and vector.distance(pos, rounded_pos) < 0.5 then - -- Reattach - staticdata.connected_at = rounded_pos - pos = rounded_pos - else - minetest.log("warning","rounded_pos="..tostring(rounded_pos)..",dist="..vector.distance(pos, rounded_pos)) - minetest.log("warning","TODO: handle detached cart behavior") - end + -- Leave nodes + if staticdata.attached_at then + handle_cart_leave(self, staticdata.attached_at, staticdata.dir ) + else + mcl_log("TODO: handle detatched minecart death") end - -- Fix railtype field - if not staticdata.railtype then - local node = minetest.get_node(vector.floor(pos)).name - staticdata.railtype = minetest.get_item_group(node, "connect_to_raillike") - end - - -- Handle punches by something other than the player - if not puncher or not puncher:is_player() then - local cart_dir = mcl_minecarts:get_rail_direction(pos, vector.new(1,0,0), nil, nil, staticdata.railtype) - if vector.equals(cart_dir, vector.new(0,0,0)) then - return + -- Drop items + local drop = def.drop + if not minetest.is_creative_enabled(killer:get_player_name()) then + for d=1, #drop do + minetest.add_item(self.object:get_pos(), drop[d]) end - - staticdata.dir = cart_dir - self._punched = true - return - end - - -- Punch+sneak: Pick up minecart (unless TNT was ignited) - if puncher:get_player_control().sneak and not self._boomtimer then - if self._driver then - if self._old_pos then - self.object:set_pos(self._old_pos) - end - detach_driver(self) - end - - -- Disable detector rail - local rou_pos = vector.round(pos) - local node = minetest.get_node(rou_pos) - if node.name == "mcl_minecarts:detector_rail_on" then - local newnode = {name="mcl_minecarts:detector_rail", param2 = node.param2} - minetest.swap_node(rou_pos, newnode) - mesecon.receptor_off(rou_pos) - end - - -- Drop items and remove cart entity - local drop = def.drop - if not minetest.is_creative_enabled(puncher:get_player_name()) then - for d=1, #drop do - minetest.add_item(self.object:get_pos(), drop[d]) - end - elseif puncher and puncher:is_player() then - local inv = puncher:get_inventory() - for d=1, #drop do - if not inv:contains_item("main", drop[d]) then - inv:add_item("main", drop[d]) - end + elseif puncher and killer:is_player() then + local inv = killer:get_inventory() + for d=1, #drop do + if not inv:contains_item("main", drop[d]) then + inv:add_item("main", drop[d]) end end - - self.object:remove() - return - end - - -- Handle player punches - local vel = self.object:get_velocity() - if puncher:get_player_name() == self._driver then - if math.abs(vel.x + vel.z) > 7 then - return - end - end - - local punch_dir = mcl_minecarts:velocity_to_dir(puncher:get_look_dir()) - punch_dir.y = 0 - - local cart_dir = mcl_minecarts:get_rail_direction(pos, punch_dir, nil, nil, self._staticdata.railtype) - if vector.equals(cart_dir, vector.new(0,0,0)) then - return - end - - staticdata.dir = cart_dir - - time_from_last_punch = math.min(time_from_last_punch, tool_capabilities.full_punch_interval) - local f = 3 * (time_from_last_punch / tool_capabilities.full_punch_interval) - - -- Perform acceleration here - staticdata.velocity = (staticdata.velocity or 0 ) + f - local max_vel = mcl_minecarts.speed_max - if staticdata.velocity > max_vel then - staticdata.velocity = max_vel end end @@ -824,6 +741,25 @@ local function register_entity(entity_id, def) self._staticdata = staticdata end + -- Regen + local hp = self.object:get_hp() + if hp < MINECART_MAX_HP then + if (staticdata.regen_timer or 0) > 0.5 then + hp = hp + 1 + staticdata.regen_timer = staticdata.regen_timer - 1 + end + staticdata.regen_timer = (staticdata.regen_timer or 0) + dtime + self.object:set_hp(hp) + else + staticdata.regen_timer = nil + end + + -- Fix railtype field + if staticdata.connected_at and not staticdata.railtype then + local node = minetest.get_node(vector.floor(pos)).name + staticdata.railtype = minetest.get_item_group(node, "connect_to_raillike") + end + -- Cart specific behaviors local hook = self._mcl_minecarts_on_step if hook then hook(self,dtime) end @@ -847,8 +783,8 @@ local function register_entity(entity_id, def) end -- Experimental controls - --self._go_forward = ctrl.up - --self._brake = ctrl.down + self._go_forward = ctrl.up + self._brake = ctrl.down end -- Give achievement when player reached a distance of 1000 nodes from the start position From fd154f7f792747edb2ea6f4e724ca752bf34331f Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 28 Mar 2024 05:49:46 +0000 Subject: [PATCH 037/195] Create mesecons command API and modify commandblock to use it --- .../REDSTONE/mesecons_commandblock/api.lua | 226 ++++++++++++++++++ .../REDSTONE/mesecons_commandblock/init.lua | 211 ++-------------- 2 files changed, 242 insertions(+), 195 deletions(-) create mode 100644 mods/ITEMS/REDSTONE/mesecons_commandblock/api.lua diff --git a/mods/ITEMS/REDSTONE/mesecons_commandblock/api.lua b/mods/ITEMS/REDSTONE/mesecons_commandblock/api.lua new file mode 100644 index 000000000..50127a2d2 --- /dev/null +++ b/mods/ITEMS/REDSTONE/mesecons_commandblock/api.lua @@ -0,0 +1,226 @@ +mesecon = mesecon or {} +local mod = {} +mesecon.commandblock = mod + +local S = minetest.get_translator(minetest.get_current_modname()) +local F = minetest.formspec_escape +local color_red = mcl_colors.RED + +mod.initialize = function(meta) + meta:set_string("commands", "") + meta:set_string("commander", "") +end + +mod.place = function(meta, placer) + if not placer then return end + + meta:set_string("commander", placer:get_player_name()) +end + +mod.resolve_commands = function(commands, meta, pos) + local players = minetest.get_connected_players() + local commander = meta:get_string("commander") + + -- A non-printable character used while replacing “@@”. + local SUBSTITUTE_CHARACTER = "\26" -- ASCII SUB + + -- No players online: remove all commands containing + -- problematic placeholders. + if #players == 0 then + commands = commands:gsub("[^\r\n]+", function (line) + line = line:gsub("@@", SUBSTITUTE_CHARACTER) + if line:find("@n") then return "" end + if line:find("@p") then return "" end + if line:find("@f") then return "" end + if line:find("@r") then return "" end + line = line:gsub("@c", commander) + line = line:gsub(SUBSTITUTE_CHARACTER, "@") + return line + end) + return commands + end + + local nearest, farthest = nil, nil + local min_distance, max_distance = math.huge, -1 + for index, player in pairs(players) do + local distance = vector.distance(pos, player:get_pos()) + if distance < min_distance then + min_distance = distance + nearest = player:get_player_name() + end + if distance > max_distance then + max_distance = distance + farthest = player:get_player_name() + end + end + local random = players[math.random(#players)]:get_player_name() + commands = commands:gsub("@@", SUBSTITUTE_CHARACTER) + commands = commands:gsub("@p", nearest) + commands = commands:gsub("@n", nearest) + commands = commands:gsub("@f", farthest) + commands = commands:gsub("@r", random) + commands = commands:gsub("@c", commander) + commands = commands:gsub(SUBSTITUTE_CHARACTER, "@") + return commands +end +local resolve_commands = mod.resolve_commands + +mod.check_commands = function(commands, player_name) + for _, command in pairs(commands:split("\n")) do + local pos = command:find(" ") + local cmd = command + if pos then + cmd = command:sub(1, pos - 1) + end + local cmddef = minetest.chatcommands[cmd] + if not cmddef then + -- Invalid chat command + local msg = S("Error: The command “@1” does not exist; your command block has not been changed. Use the “help” chat command for a list of available commands.", cmd) + if string.sub(cmd, 1, 1) == "/" then + msg = S("Error: The command “@1” does not exist; your command block has not been changed. Use the “help” chat command for a list of available commands. Hint: Try to remove the leading slash.", cmd) + end + return false, minetest.colorize(color_red, msg) + end + if player_name then + local player_privs = minetest.get_player_privs(player_name) + + for cmd_priv, _ in pairs(cmddef.privs) do + if player_privs[cmd_priv] ~= true then + local msg = S("Error: You have insufficient privileges to use the command “@1” (missing privilege: @2)! The command block has not been changed.", cmd, cmd_priv) + return false, minetest.colorize(color_red, msg) + end + end + end + end + return true +end +local check_commands = mod.check_commands + +mod.action_on = function(meta, pos) + local commander = meta:get_string("commander") + local commands = resolve_commands(meta:get_string("commands"), meta, pos) + for _, command in pairs(commands:split("\n")) do + local cpos = command:find(" ") + local cmd, param = command, "" + if cpos then + cmd = command:sub(1, cpos - 1) + param = command:sub(cpos + 1) + end + local cmddef = minetest.chatcommands[cmd] + if not cmddef then + -- Invalid chat command + return + end + -- Execute command in the name of commander + cmddef.func(commander, param) + end +end + +local formspec_metas = {} + +mod.handle_rightclick = function(meta, player) + local can_edit = true + -- Only allow write access in Creative Mode + if not minetest.is_creative_enabled(player:get_player_name()) then + can_edit = false + end + local pname = player:get_player_name() + if minetest.is_protected(pos, pname) then + can_edit = false + end + local privs = minetest.get_player_privs(pname) + if not privs.maphack then + can_edit = false + end + + local commands = meta:get_string("commands") + if not commands then + commands = "" + end + local commander = meta:get_string("commander") + local commanderstr + if commander == "" or commander == nil then + commanderstr = S("Error: No commander! Block must be replaced.") + else + commanderstr = S("Commander: @1", commander) + end + local textarea_name, submit, textarea + -- If editing is not allowed, only allow read-only access. + -- Player can still view the contents of the command block. + if can_edit then + textarea_name = "commands" + submit = "button_exit[3.3,4.4;2,1;submit;"..F(S("Submit")).."]" + else + textarea_name = "" + submit = "" + end + if not can_edit and commands == "" then + textarea = "label[0.5,0.5;"..F(S("No commands.")).."]" + else + textarea = "textarea[0.5,0.5;8.5,4;"..textarea_name..";"..F(S("Commands:"))..";"..F(commands).."]" + end + local formspec = "size[9,5;]" .. + textarea .. + submit .. + "image_button[8,4.4;1,1;doc_button_icon_lores.png;doc;]" .. + "tooltip[doc;"..F(S("Help")).."]" .. + "label[0,4;"..F(commanderstr).."]" + + -- Store the metadata object for later use + local fs_id = #formspec_metas + 1 + formspec_metas[fs_id] = meta + print("using fs_id="..tostring(fs_id)..",meta="..tostring(meta)..",formspec_metas[fs_id]="..tostring(formspec_metas[fs_id])) + + minetest.show_formspec(pname, "commandblock_"..tostring(fs_id), formspec) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if string.sub(formname, 1, 13) == "commandblock_" then + -- Show documentation + if fields.doc and minetest.get_modpath("doc") then + doc.show_entry(player:get_player_name(), "nodes", "mesecons_commandblock:commandblock_off", true) + return + end + + -- Validate form fields + if (not fields.submit and not fields.key_enter) or (not fields.commands) then + return + end + + -- Check privileges + local privs = minetest.get_player_privs(player:get_player_name()) + if not privs.maphack then + minetest.chat_send_player(player:get_player_name(), S("Access denied. You need the “maphack” privilege to edit command blocks.")) + return + end + + -- Check game mode + if not minetest.is_creative_enabled(player:get_player_name()) then + minetest.chat_send_player(player:get_player_name(), + S("Editing the command block has failed! You can only change the command block in Creative Mode!") + ) + return + end + + -- Retrieve the metadata object this formspec data belongs to + local index, _, fs_id = string.find(formname, "commandblock_(-?%d+)") + fs_id = tonumber(fs_id) + if not index or not fs_id or not formspec_metas[fs_id] then + print("index="..tostring(index)..", fs_id="..tostring(fs_id).."formspec_metas[fs_id]="..tostring(formspec_metas[fs_id])) + minetest.chat_send_player(player:get_player_name(), S("Editing the command block has failed! The command block is gone.")) + return + end + local meta = formspec_metas[fs_id] + + -- Verify the command + local check, error_message = check_commands(fields.commands, player:get_player_name()) + if check == false then + -- Command block rejected + minetest.chat_send_player(player:get_player_name(), error_message) + return + end + + -- Update the command in the metadata + meta:set_string("commands", fields.commands) + end +end) diff --git a/mods/ITEMS/REDSTONE/mesecons_commandblock/init.lua b/mods/ITEMS/REDSTONE/mesecons_commandblock/init.lua index ec41785df..3c3aa741a 100644 --- a/mods/ITEMS/REDSTONE/mesecons_commandblock/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_commandblock/init.lua @@ -1,106 +1,27 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) local S = minetest.get_translator(minetest.get_current_modname()) -local F = minetest.formspec_escape -local tonumber = tonumber - ---mesecon.command_block = {} - -local color_red = mcl_colors.RED +-- Initialize API +dofile(modpath.."/api.lua") +local api = mesecon.commandblock local command_blocks_activated = minetest.settings:get_bool("mcl_enable_commandblocks", true) local msg_not_activated = S("Command blocks are not enabled on this server") local function construct(pos) local meta = minetest.get_meta(pos) - - meta:set_string("commands", "") - meta:set_string("commander", "") + api.initialize(meta) end local function after_place(pos, placer) - if placer then - local meta = minetest.get_meta(pos) - meta:set_string("commander", placer:get_player_name()) - end + local meta = minetest.get_meta(pos) + api.place(meta, placer) end local function resolve_commands(commands, pos) - local players = minetest.get_connected_players() - local meta = minetest.get_meta(pos) - local commander = meta:get_string("commander") - - -- A non-printable character used while replacing “@@”. - local SUBSTITUTE_CHARACTER = "\26" -- ASCII SUB - - -- No players online: remove all commands containing - -- problematic placeholders. - if #players == 0 then - commands = commands:gsub("[^\r\n]+", function (line) - line = line:gsub("@@", SUBSTITUTE_CHARACTER) - if line:find("@n") then return "" end - if line:find("@p") then return "" end - if line:find("@f") then return "" end - if line:find("@r") then return "" end - line = line:gsub("@c", commander) - line = line:gsub(SUBSTITUTE_CHARACTER, "@") - return line - end) - return commands - end - - local nearest, farthest = nil, nil - local min_distance, max_distance = math.huge, -1 - for index, player in pairs(players) do - local distance = vector.distance(pos, player:get_pos()) - if distance < min_distance then - min_distance = distance - nearest = player:get_player_name() - end - if distance > max_distance then - max_distance = distance - farthest = player:get_player_name() - end - end - local random = players[math.random(#players)]:get_player_name() - commands = commands:gsub("@@", SUBSTITUTE_CHARACTER) - commands = commands:gsub("@p", nearest) - commands = commands:gsub("@n", nearest) - commands = commands:gsub("@f", farthest) - commands = commands:gsub("@r", random) - commands = commands:gsub("@c", commander) - commands = commands:gsub(SUBSTITUTE_CHARACTER, "@") - return commands -end - -local function check_commands(commands, player_name) - for _, command in pairs(commands:split("\n")) do - local pos = command:find(" ") - local cmd = command - if pos then - cmd = command:sub(1, pos - 1) - end - local cmddef = minetest.chatcommands[cmd] - if not cmddef then - -- Invalid chat command - local msg = S("Error: The command “@1” does not exist; your command block has not been changed. Use the “help” chat command for a list of available commands.", cmd) - if string.sub(cmd, 1, 1) == "/" then - msg = S("Error: The command “@1” does not exist; your command block has not been changed. Use the “help” chat command for a list of available commands. Hint: Try to remove the leading slash.", cmd) - end - return false, minetest.colorize(color_red, msg) - end - if player_name then - local player_privs = minetest.get_player_privs(player_name) - - for cmd_priv, _ in pairs(cmddef.privs) do - if player_privs[cmd_priv] ~= true then - local msg = S("Error: You have insufficient privileges to use the command “@1” (missing privilege: @2)! The command block has not been changed.", cmd, cmd_priv) - return false, minetest.colorize(color_red, msg) - end - end - end - end - return true + return api.resolve_commands(commands, meta) end local function commandblock_action_on(pos, node) @@ -109,7 +30,6 @@ local function commandblock_action_on(pos, node) end local meta = minetest.get_meta(pos) - local commander = meta:get_string("commander") if not command_blocks_activated then --minetest.chat_send_player(commander, msg_not_activated) @@ -117,22 +37,7 @@ local function commandblock_action_on(pos, node) end minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_on"}) - local commands = resolve_commands(meta:get_string("commands"), pos) - for _, command in pairs(commands:split("\n")) do - local cpos = command:find(" ") - local cmd, param = command, "" - if cpos then - cmd = command:sub(1, cpos - 1) - param = command:sub(cpos + 1) - end - local cmddef = minetest.chatcommands[cmd] - if not cmddef then - -- Invalid chat command - return - end - -- Execute command in the name of commander - cmddef.func(commander, param) - end + api.action_on(meta, pos) end local function commandblock_action_off(pos, node) @@ -146,54 +51,10 @@ local function on_rightclick(pos, node, player, itemstack, pointed_thing) minetest.chat_send_player(player:get_player_name(), msg_not_activated) return end - local can_edit = true - -- Only allow write access in Creative Mode - if not minetest.is_creative_enabled(player:get_player_name()) then - can_edit = false - end - local pname = player:get_player_name() - if minetest.is_protected(pos, pname) then - can_edit = false - end - local privs = minetest.get_player_privs(pname) - if not privs.maphack then - can_edit = false - end local meta = minetest.get_meta(pos) - local commands = meta:get_string("commands") - if not commands then - commands = "" - end - local commander = meta:get_string("commander") - local commanderstr - if commander == "" or commander == nil then - commanderstr = S("Error: No commander! Block must be replaced.") - else - commanderstr = S("Commander: @1", commander) - end - local textarea_name, submit, textarea - -- If editing is not allowed, only allow read-only access. - -- Player can still view the contents of the command block. - if can_edit then - textarea_name = "commands" - submit = "button_exit[3.3,4.4;2,1;submit;"..F(S("Submit")).."]" - else - textarea_name = "" - submit = "" - end - if not can_edit and commands == "" then - textarea = "label[0.5,0.5;"..F(S("No commands.")).."]" - else - textarea = "textarea[0.5,0.5;8.5,4;"..textarea_name..";"..F(S("Commands:"))..";"..F(commands).."]" - end - local formspec = "size[9,5;]" .. - textarea .. - submit .. - "image_button[8,4.4;1,1;doc_button_icon_lores.png;doc;]" .. - "tooltip[doc;"..F(S("Help")).."]" .. - "label[0,4;"..F(commanderstr).."]" - minetest.show_formspec(pname, "commandblock_"..pos.x.."_"..pos.y.."_"..pos.z, formspec) + api.handle_rightclick(meta, player) + end local function on_place(itemstack, placer, pointed_thing) @@ -202,12 +63,10 @@ local function on_place(itemstack, placer, pointed_thing) end -- Use pointed node's on_rightclick function first, if present - local new_stack = mcl_util.call_on_rightclick(itemstack, placer, pointed_thing) - if new_stack then - return new_stack - end - - --local node = minetest.get_node(pointed_thing.under) + local new_stack = mcl_util.call_on_rightclick(itemstack, placer, pointed_thing) + if new_stack then + return new_stack + end local privs = minetest.get_player_privs(placer:get_player_name()) if not privs.maphack then @@ -282,44 +141,6 @@ minetest.register_node("mesecons_commandblock:commandblock_on", { _mcl_hardness = -1, }) -minetest.register_on_player_receive_fields(function(player, formname, fields) - if string.sub(formname, 1, 13) == "commandblock_" then - if fields.doc and minetest.get_modpath("doc") then - doc.show_entry(player:get_player_name(), "nodes", "mesecons_commandblock:commandblock_off", true) - return - end - if (not fields.submit and not fields.key_enter) or (not fields.commands) then - return - end - - local privs = minetest.get_player_privs(player:get_player_name()) - if not privs.maphack then - minetest.chat_send_player(player:get_player_name(), S("Access denied. You need the “maphack” privilege to edit command blocks.")) - return - end - - local index, _, x, y, z = string.find(formname, "commandblock_(-?%d+)_(-?%d+)_(-?%d+)") - if index and x and y and z then - local pos = {x = tonumber(x), y = tonumber(y), z = tonumber(z)} - local meta = minetest.get_meta(pos) - if not minetest.is_creative_enabled(player:get_player_name()) then - minetest.chat_send_player(player:get_player_name(), S("Editing the command block has failed! You can only change the command block in Creative Mode!")) - return - end - local check, error_message = check_commands(fields.commands, player:get_player_name()) - if check == false then - -- Command block rejected - minetest.chat_send_player(player:get_player_name(), error_message) - return - else - meta:set_string("commands", fields.commands) - end - else - minetest.chat_send_player(player:get_player_name(), S("Editing the command block has failed! The command block is gone.")) - end - end -end) - -- Add entry alias for the Help if minetest.get_modpath("doc") then doc.add_entry_alias("nodes", "mesecons_commandblock:commandblock_off", "nodes", "mesecons_commandblock:commandblock_on") From 4c6c506713eab84d1a03238be079f9e808523b6c Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 28 Mar 2024 06:18:50 +0000 Subject: [PATCH 038/195] Implement minecart with command block --- mods/ENTITIES/mcl_minecarts/init.lua | 52 ++++++++++++++----- .../REDSTONE/mesecons_commandblock/api.lua | 1 + 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index f6d2fac02..bec5c565a 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -698,7 +698,7 @@ local function register_entity(entity_id, def) for d=1, #drop do minetest.add_item(self.object:get_pos(), drop[d]) end - elseif puncher and killer:is_player() then + elseif killer and killer:is_player() then local inv = killer:get_inventory() for d=1, #drop do if not inv:contains_item("main", drop[d]) then @@ -841,17 +841,18 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) local cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0), nil, nil, railtype) cart:set_yaw(minetest.dir_to_yaw(cart_dir)) - -- Call placer - if cart._mcl_minecarts_on_place then - cart._mcl_minecarts_on_place(self, placer) - end - -- Update static data local le = cart:get_luaentity() if le then le._staticdata = make_staticdata( railtype, railpos, cart_dir ) end + -- Call placer + if le._mcl_minecarts_on_place then + print("Calling on_place") + le._mcl_minecarts_on_place(le, placer) + end + handle_cart_enter(le, railpos) local pname = "" @@ -1118,6 +1119,20 @@ register_minecart({ end end }) +function table_metadata(table) + return { + table = table, + set_string = function(self, key, value) + print("set_string("..tostring(key)..", "..tostring(value)..")") + self.table[key] = tostring(value) + end, + get_string = function(self, key) + if self.table[key] then + return tostring(self.table[key]) + end + end + } +end -- Minecart with Command Block register_minecart({ @@ -1139,18 +1154,27 @@ register_minecart({ }, icon = "mcl_minecarts_minecart_command_block.png", drop = {"mcl_minecarts:minecart"}, - on_rightclick = nil, + on_rightclick = function(self, clicker) + self._staticdata.meta = self._staticdata.meta or {} + local meta = table_metadata(self._staticdata.meta) + + mesecon.commandblock.handle_rightclick(meta, clicker) + end, _mcl_minecarts_on_place = function(self, placer) - if mesecon and mesecon.command_block then - mesecons.command_block.configure_entity(self, placer) - end + -- Create a fake metadata object that stores into the cart's staticdata + self._staticdata.meta = self._staticdata.meta or {} + local meta = table_metadata(self._staticdata.meta) + + mesecon.commandblock.initialize(meta) + mesecon.commandblock.place(meta, placer) end, on_activate_by_rail = function(self, timer) - if mesecon and mesecond.command_block then - mesecons.command_block.resolve_command_entity(self) - end + self._staticdata.meta = self._staticdata.meta or {} + local meta = table_metadata(self._staticdata.meta) + + mesecon.commandblock.action_on(meta, self.object:get_pos()) end, - creative = false + creative = true }) -- Minecart with Hopper diff --git a/mods/ITEMS/REDSTONE/mesecons_commandblock/api.lua b/mods/ITEMS/REDSTONE/mesecons_commandblock/api.lua index 50127a2d2..7edcc9083 100644 --- a/mods/ITEMS/REDSTONE/mesecons_commandblock/api.lua +++ b/mods/ITEMS/REDSTONE/mesecons_commandblock/api.lua @@ -211,6 +211,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) return end local meta = formspec_metas[fs_id] + formspec_metas[fs_id] = nil -- Verify the command local check, error_message = check_commands(fields.commands, player:get_player_name()) From 8fafb32a5c44baf43a70a26b2729f19edf926435 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 28 Mar 2024 07:11:59 +0000 Subject: [PATCH 039/195] Start implementing new rail nodes --- mods/ENTITIES/mcl_minecarts/init.lua | 1 + mods/ENTITIES/mcl_minecarts/rails.lua | 142 ++++++++++++------ .../ENTITIES/mcl_minecarts/rails/standard.lua | 21 +++ 3 files changed, 120 insertions(+), 44 deletions(-) create mode 100644 mods/ENTITIES/mcl_minecarts/rails/standard.lua diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index bec5c565a..553200f48 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -4,6 +4,7 @@ local S = minetest.get_translator(modname) local has_mcl_wip = minetest.get_modpath("mcl_wip") mcl_minecarts = {} +local mod = mcl_minecarts mcl_minecarts.modpath = minetest.get_modpath(modname) mcl_minecarts.speed_max = 10 mcl_minecarts.check_float_time = 15 diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 802d747e4..16fedded6 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -1,50 +1,114 @@ -local S = minetest.get_translator(minetest.get_current_modname()) +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +local function drop_railcarts(pos) + -- Scan for minecarts in this pos and force them to execute their "floating" check. + -- Normally, this will make them drop. + local objs = minetest.get_objects_inside_radius(pos, 1) + for o=1, #objs do + local le = objs[o]:get_luaentity() + if le then + -- All entities in this mod are minecarts, so this works + if string.sub(le.name, 1, 14) == "mcl_minecarts:" then + le._last_float_check = mcl_minecarts.check_float_time + end + end + end +end + +local RAIL_DEFAULTS = { + is_ground_content = true, + paramtype = "light", + selection_box = { + type = "fixed", + fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, + }, + stack_max = 64, + sounds = mcl_sounds.node_sound_metal_defaults(), + _mcl_blast_resistance = 0.7, + _mcl_hardness = 0.7, + after_destruct = drop_railcarts, +} +local RAIL_DEFAULT_GROUPS = { + handy=1, pickaxey=1, + attached_node=1, + rail=1, + connect_to_raillike=minetest.raillike_group("rail"), + dig_by_water=0,destroy_by_lava_flow=0, + transport=1 +} + +local function table_merge(base, overlay) + for k,v in pairs(overlay) do + base[k] = v + end +end -- Template rail function local function register_rail(itemstring, tiles, def_extras, creative) - local groups = {handy=1,pickaxey=1, attached_node=1,rail=1,connect_to_raillike=minetest.raillike_group("rail"),dig_by_water=0,destroy_by_lava_flow=0, transport=1} + local groups = table.copy(RAIL_DEFAULT_GROUPS) if creative == false then groups.not_in_creative_inventory = 1 end local ndef = { drawtype = "raillike", tiles = tiles, - is_ground_content = false, inventory_image = tiles[1], wield_image = tiles[1], - paramtype = "light", - walkable = false, - selection_box = { - type = "fixed", - fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, - }, - stack_max = 64, groups = groups, - sounds = mcl_sounds.node_sound_metal_defaults(), - _mcl_blast_resistance = 0.7, - _mcl_hardness = 0.7, - after_destruct = function(pos) - -- Scan for minecarts in this pos and force them to execute their "floating" check. - -- Normally, this will make them drop. - local objs = minetest.get_objects_inside_radius(pos, 1) - for o=1, #objs do - local le = objs[o]:get_luaentity() - if le then - -- All entities in this mod are minecarts, so this works - if string.sub(le.name, 1, 14) == "mcl_minecarts:" then - le._last_float_check = mcl_minecarts.check_float_time - end - end - end - end, } - if def_extras then - for k,v in pairs(def_extras) do - ndef[k] = v - end - end + table_merge(ndef, RAIL_DEFAULTS) + ndef.walkable = false -- Old behavior + table_merge(ndef, def_extras) minetest.register_node(itemstring, ndef) end +local function register_rail_v2(itemstring, def) + assert(def.tiles) + + -- Extract out the craft recipe + local craft = def.craft + def.craft = nil + + -- Build rail groups + local groups = table.copy(RAIL_DEFAULT_GROUPS) + if def.groups then table.merge(groups, def.groups) end + def.groups = groups + + -- Build the node definition + local ndef = { + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-8/16, -8/16, -8/16, 8/16, -7/16, 8/15} + } + } + } + table_merge(ndef, RAIL_DEFAULTS) + table_merge(ndef, def) + + -- Add sensible defaults + if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end + if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end + + -- Make registrations + minetest.register_node(itemstring, ndef) + minetest.register_craft(craft) +end +mod.register_rail = register_rail_v2 + +-- Setup shared text +local railuse = S( + "Place them on the ground to build your railway, the rails will automatically connect to each other and will".. + " turn into curves, T-junctions, crossings and slopes as needed." +) +mod.text = mod.text or {} +mod.text.railuse = railuse + +-- Register rails +dofile(modpath.."/rails/standard.lua") -- Redstone rules local rail_rules_long = @@ -66,8 +130,6 @@ local rail_rules_long = local rail_rules_short = mesecon.rules.pplate -local railuse = S("Place them on the ground to build your railway, the rails will automatically connect to each other and will turn into curves, T-junctions, crossings and slopes as needed.") - -- Normal rail register_rail("mcl_minecarts:rail", {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"}, @@ -86,7 +148,8 @@ register_rail("mcl_minecarts:golden_rail", description = S("Powered Rail"), _tt_help = S("Track for minecarts").."\n"..S("Speed up when powered, slow down when not powered"), _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts."), - _doc_items_usagehelp = railuse .. "\n" .. S("Without redstone power, the rail will brake minecarts. To make this rail accelerate minecarts, power it with redstone power."), + _doc_items_usagehelp = railuse .. "\n" .. S("Without redstone power, the rail will brake minecarts. To make this rail accelerate".. + " minecarts, power it with redstone power."), _rail_acceleration = -3, mesecons = { conductor = { @@ -247,15 +310,6 @@ register_rail("mcl_minecarts:detector_rail_on", -- Crafting -minetest.register_craft({ - output = "mcl_minecarts:rail 16", - recipe = { - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - } -}) - minetest.register_craft({ output = "mcl_minecarts:golden_rail 6", recipe = { diff --git a/mods/ENTITIES/mcl_minecarts/rails/standard.lua b/mods/ENTITIES/mcl_minecarts/rails/standard.lua new file mode 100644 index 000000000..738137907 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/rails/standard.lua @@ -0,0 +1,21 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +mod.register_rail("mcl_minecarts:rail_v2", { + tiles = {"default_rail.png"}, + description = S("Rail"), + _tt_help = S("Track for minecarts"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), + _doc_items_usagehelp = mod.text.railuse, + craft = { + output = "mcl_minecarts:rail_v2 16", + recipe = { + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + } + } +}) + From 2151644b1ca661f31bbe22b37fb513c9aba09393 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 28 Mar 2024 15:06:26 +0000 Subject: [PATCH 040/195] Implement initial rail connection logic (no vertical track yet), experiment with texture modifiers and gravel underlay for display (not working) --- mods/ENTITIES/mcl_minecarts/init.lua | 2 +- mods/ENTITIES/mcl_minecarts/rails.lua | 11 +- .../ENTITIES/mcl_minecarts/rails/standard.lua | 178 +++++++++++++++++- 3 files changed, 183 insertions(+), 8 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 553200f48..4b25c2b0f 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -948,7 +948,7 @@ local function register_minecart(def) register_entity(def.entity_id, def) register_craftitem(def.itemstring, def.entity_id, def.description, def.tt_help, def.longdesc, def.usagehelp, def.icon, def.creative) if minetest.get_modpath("doc_identifier") then - doc.sub.identifier.register_object(def.entity_id, "craftitems", itemstring) + doc.sub.identifier.register_object(def.entity_id, "craftitems", def.itemstring) end end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 16fedded6..970598971 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -1,6 +1,10 @@ local modname = minetest.get_current_modname() local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts +mod.RAIL_GROUPS = { + STANDARD = 1, + CURVES = 2, +} local S = minetest.get_translator(modname) local function drop_railcarts(pos) @@ -73,9 +77,10 @@ local function register_rail_v2(itemstring, def) -- Build rail groups local groups = table.copy(RAIL_DEFAULT_GROUPS) - if def.groups then table.merge(groups, def.groups) end + if def.groups then table_merge(groups, def.groups) end def.groups = groups + -- Build the node definition local ndef = { drawtype = "nodebox", @@ -93,9 +98,11 @@ local function register_rail_v2(itemstring, def) if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end + print("registering rail "..itemstring.." with definition: "..dump(ndef)) + -- Make registrations minetest.register_node(itemstring, ndef) - minetest.register_craft(craft) + if craft then minetest.register_craft(craft) end end mod.register_rail = register_rail_v2 diff --git a/mods/ENTITIES/mcl_minecarts/rails/standard.lua b/mods/ENTITIES/mcl_minecarts/rails/standard.lua index 738137907..469ac1762 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/standard.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/standard.lua @@ -1,21 +1,189 @@ local modname = minetest.get_current_modname() local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts -local S = minetest.get_translator(modname) -mod.register_rail("mcl_minecarts:rail_v2", { - tiles = {"default_rail.png"}, +-- This is a candidate for adding to mcl_util +local function table_merge(base, overlay) + for k,v in pairs(overlay) do + if type(base[k]) == "table" then + table_merge(base[k], v) + else + base[k] = v + end + end + return base +end + +local north = vector.new( 0, 0, 1); local N = 1 +local south = vector.new( 0, 0,-1); local S = 2 -- Note: this is overwritten below +local east = vector.new( 1, 0, 0); local E = 4 +local west = vector.new(-1, 0, 0); local W = 8 + +local HORIZONTAL_CONNECTIONS = { north, south, east, west } +local HORIZONTAL_STANDARD_MAPPINGS = { + [N] = "_ns", + [S] = "_ns", + [N+S] = "_ns", + + [E] = "_ew", + [W] = "_ew", + [E+W] = "_ew", +} +local HORIZONTAL_CURVES_MAPPINGS = { + [N+E] = "_corner_ne", + [N+W] = "_corner_nw", + [S+E] = "_corner_se", + [S+W] = "_corner_sw", + + [N+E+W] = "_tee_new_off", + [S+E+W] = "_tee_sew_off", + [N+S+E] = "_tee_nse_off", + [N+S+W] = "_tee_nsw_off", + +-- [N+S+E+W] = "_cross", +} +table_merge(HORIZONTAL_CURVES_MAPPINGS, HORIZONTAL_STANDARD_MAPPINGS) +local HORIZONTAL_MAPPINGS_BY_RAIL_GROUP = { + [1] = HORIZONTAL_STANDARD_MAPPINGS, + [2] = HORIZONTAL_CURVES_MAPPINGS, +} +print(dump(HORIZONTAL_MAPPINGS_BY_RAIL_GROUP)) +local DIRECTION_BITS = {N, S, E, W} + +local function update_rail_connections(pos, update_neighbors) + local node = minetest.get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + if not nodedef._mcl_minecarts then + minetest.log("warning", "attemting to rail connect "..node.name) + return + end + + -- Get the mappings to use + local mappings = HORIZONTAL_MAPPINGS_BY_RAIL_GROUP[nodedef.groups.rail] + if not mappings then return end + + -- Horizontal rules, Check for rails on each neighbor + local connections = 0 + for i = 1,4 do + local neighbor = vector.add(pos, HORIZONTAL_CONNECTIONS[i]) + local node = minetest.get_node(neighbor) + local nodedef = minetest.registered_nodes[node.name] + + if nodedef.groups.rail then + connections = connections + DIRECTION_BITS[i] + end + + if update_neighbors then + update_rail_connections(neighbor, false) + end + end + + local mapping = mappings[connections] + if mapping then + local new_name = nodedef._mcl_minecarts.base_name..mapping + if new_name ~= node.name then + print("swapping "..node.name.." for "..new_name.." at "..tostring(pos)) + node.name = new_name + minetest.swap_node(pos, node) + end + end +end +mod.update_rail_connections = update_rail_connections + +-- Now get the translator after we have finished using S for other things +local S = minetest.get_translator(modname) +local BASE_DEF = { description = S("Rail"), _tt_help = S("Track for minecarts"), _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), _doc_items_usagehelp = mod.text.railuse, + groups = { + rail = mod.RAIL_GROUPS.CURVES, + }, + after_place_node = function(pos, placer, itemstack, pointed_thing) + update_rail_connections(pos, true) + end, +} +local CORNERS = { + { "nw", "I" }, + { "ne", "R90" }, + { "se", "R180" }, + { "sw", "R270" }, +} +local TEES = { + { "nse", "I", "FX" }, + { "nsw", "R90", "FXR90" }, + { "new", "R180", "FY" }, + { "sew", "R270", "FYR90" }, +} +local function register_curves_rail(base_name, tiles, def) + def = def or {} + local base_def = table.copy(BASE_DEF) + table_merge(base_def,{ + _mcl_minecarts = { base_name = base_name }, + drop = base_name.."_ns", + }) + table_merge(base_def, def) + + -- Register the base node + mod.register_rail(base_name.."_ns", table_merge(table.copy(base_def),{ + tiles = {"default_gravel.png^"..tiles[1]}, + })) + BASE_DEF.craft = nil + + -- East-west variant + mod.register_rail(base_name.."_ew", table_merge(table.copy(base_def),{ + tiles = { "default_gravel.png^[transformR90:"..tiles[1].."" }, + groups = { + not_in_creative_inventory = 1, + }, + })) + + -- Corner variants + for _,c in ipairs(CORNERS) do + mod.register_rail(base_name.."_corner_"..c[1], table_merge(table.copy(base_def),{ + tiles = { "default_gravel.png^[transform"..c[2]..":"..tiles[2] }, + groups = { + not_in_creative_inventory = 1, + }, + })) + end + + -- Tee variants + for _,t in ipairs(TEES) do + mod.register_rail(base_name.."_tee_"..t[1].."_off", table_merge(table.copy(base_def),{ + tiles = { "default_gravel.png^[transform"..t[2]..":"..tiles[3] }, + groups = { + not_in_creative_inventory = 1, + }, + })) + mod.register_rail(base_name.."_tee_"..t[1].."_on", table_merge(table.copy(base_def),{ + tiles = { "default_gravel.png^[transform"..t[3]..":"..tiles[3] }, + groups = { + not_in_creative_inventory = 1, + }, + })) + end + + -- Cross variant + --[[ + mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{ + tiles = { tiles[4] }, + groups = { + not_in_creative_inventory = 1, + }, + })) + ]] +end +mod.register_curves_rail = register_curves_rail +register_curves_rail("mcl_minecarts:rail", {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"},{ craft = { - output = "mcl_minecarts:rail_v2 16", + output = "mcl_minecarts:rail_ns 16", recipe = { {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, } - } + }, }) From b870fcebd09ffde194350d3bb3b6f3b3de331561 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 29 Mar 2024 18:08:07 +0000 Subject: [PATCH 041/195] Fix rail visuals, add switch operation --- .../ENTITIES/mcl_minecarts/rails/standard.lua | 125 +++++++++--------- textures/default_rail_t_junction_on.png | Bin 0 -> 4375 bytes 2 files changed, 64 insertions(+), 61 deletions(-) create mode 100644 textures/default_rail_t_junction_on.png diff --git a/mods/ENTITIES/mcl_minecarts/rails/standard.lua b/mods/ENTITIES/mcl_minecarts/rails/standard.lua index 469ac1762..5e3378d66 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/standard.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/standard.lua @@ -21,24 +21,24 @@ local west = vector.new(-1, 0, 0); local W = 8 local HORIZONTAL_CONNECTIONS = { north, south, east, west } local HORIZONTAL_STANDARD_MAPPINGS = { - [N] = "_ns", - [S] = "_ns", - [N+S] = "_ns", + [N] = { "", 0 }, + [S] = { "", 0 }, + [N+S] = { "", 0 }, - [E] = "_ew", - [W] = "_ew", - [E+W] = "_ew", + [E] = { "", 1 }, + [W] = { "", 1 }, + [E+W] = { "", 1 }, } local HORIZONTAL_CURVES_MAPPINGS = { - [N+E] = "_corner_ne", - [N+W] = "_corner_nw", - [S+E] = "_corner_se", - [S+W] = "_corner_sw", + [N+E] = { "_corner", 3 }, + [N+W] = { "_corner", 2 }, + [S+E] = { "_corner", 0 }, + [S+W] = { "_corner", 1 }, - [N+E+W] = "_tee_new_off", - [S+E+W] = "_tee_sew_off", - [N+S+E] = "_tee_nse_off", - [N+S+W] = "_tee_nsw_off", + [N+E+W] = { "_tee_off", 3 }, + [S+E+W] = { "_tee_off", 1 }, + [N+S+E] = { "_tee_off", 0 }, + [N+S+W] = { "_tee_off", 2 }, -- [N+S+E+W] = "_cross", } @@ -80,10 +80,11 @@ local function update_rail_connections(pos, update_neighbors) local mapping = mappings[connections] if mapping then - local new_name = nodedef._mcl_minecarts.base_name..mapping - if new_name ~= node.name then - print("swapping "..node.name.." for "..new_name.." at "..tostring(pos)) + local new_name = nodedef._mcl_minecarts.base_name..mapping[1] + if new_name ~= node.name or node.param2 ~= mapping[2] then + print("swapping "..node.name.." for "..new_name..","..tostring(mapping[2]).." at "..tostring(pos)) node.name = new_name + node.param2 = mapping[2] minetest.swap_node(pos, node) end end @@ -100,70 +101,66 @@ local BASE_DEF = { groups = { rail = mod.RAIL_GROUPS.CURVES, }, + paramtype = "light", + paramtype2 = "facedir", after_place_node = function(pos, placer, itemstack, pointed_thing) update_rail_connections(pos, true) end, } -local CORNERS = { - { "nw", "I" }, - { "ne", "R90" }, - { "se", "R180" }, - { "sw", "R270" }, -} -local TEES = { - { "nse", "I", "FX" }, - { "nsw", "R90", "FXR90" }, - { "new", "R180", "FY" }, - { "sew", "R270", "FYR90" }, -} local function register_curves_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) table_merge(base_def,{ _mcl_minecarts = { base_name = base_name }, - drop = base_name.."_ns", + drop = base_name, }) table_merge(base_def, def) -- Register the base node - mod.register_rail(base_name.."_ns", table_merge(table.copy(base_def),{ - tiles = {"default_gravel.png^"..tiles[1]}, + mod.register_rail(base_name, table_merge(table.copy(base_def),{ + tiles = { tiles[1] }, })) BASE_DEF.craft = nil - -- East-west variant - mod.register_rail(base_name.."_ew", table_merge(table.copy(base_def),{ - tiles = { "default_gravel.png^[transformR90:"..tiles[1].."" }, + -- Corner variants + mod.register_rail(base_name.."_corner", table_merge(table.copy(base_def),{ + tiles = { tiles[2] }, groups = { not_in_creative_inventory = 1, }, })) - -- Corner variants - for _,c in ipairs(CORNERS) do - mod.register_rail(base_name.."_corner_"..c[1], table_merge(table.copy(base_def),{ - tiles = { "default_gravel.png^[transform"..c[2]..":"..tiles[2] }, - groups = { - not_in_creative_inventory = 1, - }, - })) - end - -- Tee variants - for _,t in ipairs(TEES) do - mod.register_rail(base_name.."_tee_"..t[1].."_off", table_merge(table.copy(base_def),{ - tiles = { "default_gravel.png^[transform"..t[2]..":"..tiles[3] }, - groups = { - not_in_creative_inventory = 1, - }, - })) - mod.register_rail(base_name.."_tee_"..t[1].."_on", table_merge(table.copy(base_def),{ - tiles = { "default_gravel.png^[transform"..t[3]..":"..tiles[3] }, - groups = { - not_in_creative_inventory = 1, - }, - })) - end + mod.register_rail(base_name.."_tee_off", table_merge(table.copy(base_def),{ + tiles = { tiles[3] }, + groups = { + not_in_creative_inventory = 1, + }, + mesecons = { + effector = { + action_on = function(pos, node) + local new_node = {name = base_name.."_tee_on", param2 = node.param2} + minetest.swap_node(pos, new_node) + end, + rules = mesecon.rules.alldirs, + } + } + })) + mod.register_rail(base_name.."_tee_on", table_merge(table.copy(base_def),{ + tiles = { tiles[4] }, + groups = { + not_in_creative_inventory = 1, + }, + mesecons = { + effector = { + action_off = function(pos, node) + local new_node = {name = base_name.."_tee_off", param2 = node.param2} + minetest.swap_node(pos, new_node) + end, + rules = mesecon.rules.alldirs, + } + } + })) -- Cross variant --[[ @@ -176,9 +173,15 @@ local function register_curves_rail(base_name, tiles, def) ]] end mod.register_curves_rail = register_curves_rail -register_curves_rail("mcl_minecarts:rail", {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"},{ +register_curves_rail("mcl_minecarts:rail_v2", { + "default_rail.png", + "default_rail_curved.png", + "default_rail_t_junction.png", + "default_rail_t_junction_on.png", + "default_rail_crossing.png" +},{ craft = { - output = "mcl_minecarts:rail_ns 16", + output = "mcl_minecarts:rail_v2 16", recipe = { {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, diff --git a/textures/default_rail_t_junction_on.png b/textures/default_rail_t_junction_on.png new file mode 100644 index 0000000000000000000000000000000000000000..b605180e159b1b29cf621588f822bbb878c136f6 GIT binary patch literal 4375 zcmeHKdu$xV8Q-(x^VP9YUN~;ri0vMEIAm|1=eynb9AclHTg7*F?JESAD&E_jy)3@R z=5B4DLsA8dX%h0Fv{6Hp5TvFl#9)<1m8fY!l`Uv#QX!QRs1wIZ)i`QJLlh`NEzocG z&My_IvQ+-_S#$Hv%x`}4&2PT>&U2tOvbCaYRT+k16;deJ2K5rFEGmZY_pe;DI^IIt zB~6kxU@m9?(y<70SQsddS$2`57_(+Q4rLkG7RD;893^&r2<)#F>>q-?zJMtLn{gCj z6;Pjq!h*d5>T^)^@8#Ee6>RI{vj0M0S+yvmS=_a5XTWW_gk3L{`8VAAE}( z2WZE$Gagw?_l&*q4zTp=4GpbQLj!JP(wd$?7?wM^_xpj+Nb8D$miC_zca}b}f4%x# zr01JU-{=@1qOM=m58kns`m}#-g>>+VA5U}`AH-k!+r+gO2Kxtb*Ju0uoBY+op#vYU z8K`9HUjEh5+vCFJ-n(#qL-YCJYx~ZQY2wpM&j0dT%!dc@CMLAzw-?y!cUPBO{iypW zcJ*eg_V=HUpB`+v`0aAn{?T39@tRxCTNlnIjq!Cye)iN0%T6zEIsP_&%YEt2qgVYa zzf+mhMlYP1Jm4OUz0`l~4sA`l`~>}R#Wv-|x=$95^zB7Y|2^ss z#g=Y+60hGra@Ri9cfm7LkeH`if z?&i#6nX{v(M#mupb9!{Uxjo!0D(R$0R?{)$$t4Zzys+BMIYU;ukcr1oTu%jvo3Fe^ z;JO+h?)Qf2u+e~a>Y?2k)V@0sRd#nNf=X~+v=tW z+=iH40b)CZQA0X|aNfgvXtFV<_pn4=8D5)FHL)$&RG@%XfY@o8hDcGpy}h1Z&Xdl> zDMk_QZ`%8zR{gY z*xOMR3MEhypjlXznX{yI;ihL;62$eSVb21w=O9g8n-Ob{Z`O#to!O4S^l9EX=&8Ni zU_gb#Vlb_ATkc800AZ~!s%b@6Mcd?LzhC7QniOb`BY8$fq|7iHDf7Hadr=Hh1UrjL zN@Yzsr67w6$UQpn2%Ji5s757Yg20lzO#8{0PvuD;rzptB37ikjrfA9N5S4ObwpSLF z3RE)UX;j11kbW9zB=2SYr0ny0Nj6ravaFY8ecn8ks)$YLOj3s9)RS@?QAR3myJ-n7 zZfTVQ1nZ$^dRh~*slkK*(X6Msb29@`J&D>)*;147F^td4a6UgQ;}f_U(0wSAg-EnG z?bKUCvfG5l$~-ajv#v!!gZ=&|H4pURaNlSkb=s`NWO-aNm&yF68Tk4hzT6WAbu`; zHm#Yxat3XQgN~pTgs1%jg|D{{s(LQEcPFw=0R$syj%0-?!TbfmsKSORD`!mYxR&}a zO=@kxtRw^T@_mrJAQw{8$*`cAm3F?wPvI=S#1(*gVUc<1yCBzsT=P<3Uf_l3T99jA z3d{?<5MBQ_xyojqQz!-h1@*#XspG+m@Fd3WZf%Y>+D|S_k|ej=y>a74uh$z01a{pQ z+_R(kz(bMPwoT9Ubw2y>Yhl1S%(h57cJK9fHLDTZwl#?DFIjE9&A(!V9>`+Y;^kIx zU?)yiL1(Ecg&Rw66tAvaz4rQ_Kllu~_DaDmQP(Ak0_&AOokOo5 zDNjUx@YDE-udd>Z{STR@xhm4x>Eb&lj;CDFl{ZG;dY~lcFFC)CqDz<{IaqaXdAwHNdQ?+$z3+_1dr-ADg8wz&B4;cxUX!(}Y(dL$X&$6<$GuX^&I S;rd(P2qrZ~f Date: Fri, 29 Mar 2024 19:53:13 +0000 Subject: [PATCH 042/195] Add sloped rail --- .../mcl_minecarts/models/sloped_track.obj | 15 +++++++ mods/ENTITIES/mcl_minecarts/rails.lua | 44 ++++++++++++++++++- .../ENTITIES/mcl_minecarts/rails/standard.lua | 6 ++- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 mods/ENTITIES/mcl_minecarts/models/sloped_track.obj diff --git a/mods/ENTITIES/mcl_minecarts/models/sloped_track.obj b/mods/ENTITIES/mcl_minecarts/models/sloped_track.obj new file mode 100644 index 000000000..86edb4b58 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/models/sloped_track.obj @@ -0,0 +1,15 @@ +# hand-made Wavefront .OBJ file for sloped rail +mtllib mcl_minecarts_rail.mtl +o sloped_rail.001 +v -0.500000 -0.500000 -0.500000 +v -0.500000 0.500000 0.500000 +v 0.500000 0.500000 0.500000 +v 0.500000 -0.500000 -0.500000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vn 0.707106 0.707106 0.000000 +usemtl None +s off +f 1/1/1 2/2/1 3/3/1 4/4/1 diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 970598971..3ec8980b2 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -80,7 +80,6 @@ local function register_rail_v2(itemstring, def) if def.groups then table_merge(groups, def.groups) end def.groups = groups - -- Build the node definition local ndef = { drawtype = "nodebox", @@ -106,6 +105,49 @@ local function register_rail_v2(itemstring, def) end mod.register_rail = register_rail_v2 + +local function register_rail_sloped(itemstring, def) + assert(def.tiles) + + -- Build rail groups + local groups = table.copy(RAIL_DEFAULT_GROUPS) + if def.groups then table_merge(groups, def.groups) end + def.groups = groups + + -- Build the node definition + local ndef = table.copy(RAIL_DEFAULTS) + table_merge(ndef,{ + drawtype = "mesh", + mesh = "sloped_track.obj", + collision_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.0, 0.5 }, + { -0.5, 0.0, 0.0, 0.5, 0.5, 0.5 } + } + }, + selection_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.0, 0.5 }, + { -0.5, 0.0, 0.0, 0.5, 0.5, 0.5 } + } + } + }) + table_merge(ndef, def) + + -- Add sensible defaults + if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end + if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end + + print("registering sloped rail "..itemstring.." with definition: "..dump(ndef)) + + -- Make registrations + minetest.register_node(itemstring, ndef) + if craft then minetest.register_craft(craft) end +end +mod.register_rail_sloped = register_rail_sloped + -- Setup shared text local railuse = S( "Place them on the ground to build your railway, the rails will automatically connect to each other and will".. diff --git a/mods/ENTITIES/mcl_minecarts/rails/standard.lua b/mods/ENTITIES/mcl_minecarts/rails/standard.lua index 5e3378d66..e5aa00778 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/standard.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/standard.lua @@ -94,7 +94,7 @@ mod.update_rail_connections = update_rail_connections -- Now get the translator after we have finished using S for other things local S = minetest.get_translator(modname) local BASE_DEF = { - description = S("Rail"), + description = S("New Rail"), -- Temporary name to make debugging easier _tt_help = S("Track for minecarts"), _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), _doc_items_usagehelp = mod.text.railuse, @@ -161,6 +161,10 @@ local function register_curves_rail(base_name, tiles, def) } } })) + mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ + description = S("Sloped Rail"), -- Temporary name to make debugging easier + tiles = { tiles[1] }, + })) -- Cross variant --[[ From 6a9080844eb1561f85f4fb2ad136a43c585a23c1 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 30 Mar 2024 22:02:06 +0000 Subject: [PATCH 043/195] Change connection rules again to allow building parallel track, tees and crosses), start implementing rail rules callbacks --- .../ENTITIES/mcl_minecarts/rails/standard.lua | 181 +++++++++++++----- 1 file changed, 135 insertions(+), 46 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails/standard.lua b/mods/ENTITIES/mcl_minecarts/rails/standard.lua index e5aa00778..db757bb6d 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/standard.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/standard.lua @@ -15,40 +15,55 @@ local function table_merge(base, overlay) end local north = vector.new( 0, 0, 1); local N = 1 -local south = vector.new( 0, 0,-1); local S = 2 -- Note: this is overwritten below +local south = vector.new( 0, 0,-1); local S = 2 -- Note: S is overwritten below with the translator local east = vector.new( 1, 0, 0); local E = 4 local west = vector.new(-1, 0, 0); local W = 8 -local HORIZONTAL_CONNECTIONS = { north, south, east, west } -local HORIZONTAL_STANDARD_MAPPINGS = { - [N] = { "", 0 }, - [S] = { "", 0 }, - [N+S] = { "", 0 }, +local CONNECTIONS = { north, south, east, west } +local HORIZONTAL_STANDARD_RULES = { + [N] = { "", 0, mask = N, score = 1 }, + [S] = { "", 0, mask = S, score = 1 }, + [N+S] = { "", 0, mask = N+S, score = 2 }, - [E] = { "", 1 }, - [W] = { "", 1 }, - [E+W] = { "", 1 }, + [E] = { "", 1, mask = E, score = 1 }, + [W] = { "", 1, mask = W, score = 1 }, + [E+W] = { "", 1, mask = E+W, score = 2 }, } -local HORIZONTAL_CURVES_MAPPINGS = { - [N+E] = { "_corner", 3 }, - [N+W] = { "_corner", 2 }, - [S+E] = { "_corner", 0 }, - [S+W] = { "_corner", 1 }, - [N+E+W] = { "_tee_off", 3 }, - [S+E+W] = { "_tee_off", 1 }, - [N+S+E] = { "_tee_off", 0 }, - [N+S+W] = { "_tee_off", 2 }, +local HORIZONTAL_CURVES_RULES = { + [N+E] = { "_corner", 3, name = "ne corner", mask = N+E, score = 3 }, + [N+W] = { "_corner", 2, name = "nw corner", mask = N+W, score = 3 }, + [S+E] = { "_corner", 0, name = "se corner", mask = S+E, score = 3 }, + [S+W] = { "_corner", 1, name = "sw corner", mask = S+W, score = 3 }, --- [N+S+E+W] = "_cross", + [N+E+W] = { "_tee_off", 3, mask = N+E+W, score = 4 }, + [S+E+W] = { "_tee_off", 1, mask = S+E+W, score = 4 }, + [N+S+E] = { "_tee_off", 0, mask = N+S+E, score = 4 }, + [N+S+W] = { "_tee_off", 2, mask = N+S+W, score = 4 }, + + [N+S+E+W] = { "_cross", 0, mask = N+S+E+W, score = 5 }, } -table_merge(HORIZONTAL_CURVES_MAPPINGS, HORIZONTAL_STANDARD_MAPPINGS) -local HORIZONTAL_MAPPINGS_BY_RAIL_GROUP = { - [1] = HORIZONTAL_STANDARD_MAPPINGS, - [2] = HORIZONTAL_CURVES_MAPPINGS, + +table_merge(HORIZONTAL_CURVES_RULES, HORIZONTAL_STANDARD_RULES) +local HORIZONTAL_RULES_BY_RAIL_GROUP = { + [1] = HORIZONTAL_STANDARD_RULES, + [2] = HORIZONTAL_CURVES_RULES, } -print(dump(HORIZONTAL_MAPPINGS_BY_RAIL_GROUP)) -local DIRECTION_BITS = {N, S, E, W} + +local function check_connection_rule(pos, connections, rule) + -- All bits in the mask must be set for the connection to be possible + if bit.band(rule.mask,connections) ~= rule.mask then + --print("Mask mismatch ("..tostring(rule.mask)..","..tostring(connections)..")") + return false + end + + -- If there is an allow filter, that mush also return true + if rule.allow and rule.allow(rule, connections, pos) then + return false + end + + return true +end local function update_rail_connections(pos, update_neighbors) local node = minetest.get_node(pos) @@ -59,38 +74,105 @@ local function update_rail_connections(pos, update_neighbors) end -- Get the mappings to use - local mappings = HORIZONTAL_MAPPINGS_BY_RAIL_GROUP[nodedef.groups.rail] - if not mappings then return end + local rules = HORIZONTAL_RULES_BY_RAIL_GROUP[nodedef.groups.rail] + if nodedef._mcl_minecarts and nodedef._mcl_minecarts.connection_rules then -- Custom connection rules + rules = nodedef._mcl_minecarts.connection_rules + end + if not rules then return end -- Horizontal rules, Check for rails on each neighbor local connections = 0 - for i = 1,4 do - local neighbor = vector.add(pos, HORIZONTAL_CONNECTIONS[i]) + for i,dir in ipairs(CONNECTIONS) do + local neighbor = vector.add(pos, dir) local node = minetest.get_node(neighbor) local nodedef = minetest.registered_nodes[node.name] - if nodedef.groups.rail then - connections = connections + DIRECTION_BITS[i] - end - - if update_neighbors then - update_rail_connections(neighbor, false) + -- TODO: modify to only allow connections to the ends of rails (direction rules) + if (nodedef.groups or {}).rail and nodedef._mcl_minecarts and nodedef._mcl_minecarts.get_next_dir then + local diff = vector.direction(neighbor, pos) + local next_dir = nodedef._mcl_minecarts.get_next_dir(neighbor, diff, node) + if next_dir == diff then + connections = connections + bit.lshift(1,i - 1) + end end end - local mapping = mappings[connections] - if mapping then - local new_name = nodedef._mcl_minecarts.base_name..mapping[1] - if new_name ~= node.name or node.param2 ~= mapping[2] then - print("swapping "..node.name.." for "..new_name..","..tostring(mapping[2]).." at "..tostring(pos)) - node.name = new_name - node.param2 = mapping[2] - minetest.swap_node(pos, node) + -- Select the best allowed connection + local rule = nil + local score = 0 + for k,r in pairs(rules) do + if check_connection_rule(pos, connections, r) then + if r.score > score then + --print("Best rule so far is "..dump(r)) + score = r.score + rule = r + end end end + if not rule then return end + + -- Apply the mapping + local new_name = nodedef._mcl_minecarts.base_name..rule[1] + if new_name ~= node.name or node.param2 ~= rule[2] then + print("swapping "..node.name.." for "..new_name..","..tostring(rule[2]).." at "..tostring(pos)) + node.name = new_name + node.param2 = rule[2] + minetest.swap_node(pos, node) + end + + if rule.after then + rule.after(rule, pos, connections) + end end mod.update_rail_connections = update_rail_connections +local function rail_dir_straight(pos, dir, node) + if node.param2 == 0 or node.param2 == 2 then + if vector.equals(dir, north) then + return north + else + return south + end + else + if vector.equals(dir,east) then + return east + else + return west + end + end +end +local function rail_dir_curve(pos, dir, node) + if node.param2 == 0 then + -- South and East + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return east end + if vector.equals(dir, west) then return south end + if vector.equals(dir, east) then return east end + elseif node.param2 == 1 then + -- South and West + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return west end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return south end + elseif node.param2 == 2 then + -- North and West + if vector.equals(dir, south) then return west end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return north end + elseif node.param2 == 3 then + -- North and East + if vector.equals(dir, south) then return east end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return north end + if vector.equals(dir, east) then return east end + end +end + +local function rail_dir_cross(pos, dir, node) + -- Always continue in the same direction. No direction changes allowed + return dir +end -- Now get the translator after we have finished using S for other things local S = minetest.get_translator(modname) local BASE_DEF = { @@ -119,12 +201,18 @@ local function register_curves_rail(base_name, tiles, def) -- Register the base node mod.register_rail(base_name, table_merge(table.copy(base_def),{ tiles = { tiles[1] }, + _mcl_minecarts = { + get_next_dir = rail_dir_straight + } })) BASE_DEF.craft = nil -- Corner variants mod.register_rail(base_name.."_corner", table_merge(table.copy(base_def),{ tiles = { tiles[2] }, + _mcl_minecarts = { + get_next_dir = rail_dir_curve, + }, groups = { not_in_creative_inventory = 1, }, @@ -163,18 +251,19 @@ local function register_curves_rail(base_name, tiles, def) })) mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ description = S("Sloped Rail"), -- Temporary name to make debugging easier + _mcl_minecarts = { + get_next_dir = rail_dir_cross, + }, tiles = { tiles[1] }, })) -- Cross variant - --[[ mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{ - tiles = { tiles[4] }, + tiles = { tiles[5] }, groups = { not_in_creative_inventory = 1, }, })) - ]] end mod.register_curves_rail = register_curves_rail register_curves_rail("mcl_minecarts:rail_v2", { From e1c4606e8f449f9aed321ad0ec6aa91e57747ccb Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 30 Mar 2024 22:33:40 +0000 Subject: [PATCH 044/195] Enable new track with get_next_dir handlers --- mods/ENTITIES/mcl_minecarts/functions.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 3aff98b81..8c025ae93 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -110,6 +110,13 @@ local diagonal_convert = { function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) local pos = vector.round(pos_) + -- Handle new track types that have track-specific direction handler + local node = minetest.get_node(pos) + local node_def = minetest.registered_nodes[node.name] + if node_def and node_def._mcl_minecarts and node_def._mcl_minecarts.get_next_dir then + return node_def._mcl_minecarts.get_next_dir(pos, dir, node) + end + -- Diagonal conversion local checks = rail_checks if dir.x ~= 0 and dir.z ~= 0 then From 06c1b362b2e1dda6469fb98265c142eef0032a70 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 30 Mar 2024 23:00:17 +0000 Subject: [PATCH 045/195] Finish reverting 08b41a3b392005e7078b09d68854e6df562d5fd7 --- mods/ENTITIES/mcl_minecarts/rails.lua | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 3ec8980b2..ec076cbc8 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -225,22 +225,6 @@ register_rail("mcl_minecarts:golden_rail_on", onstate = "mcl_minecarts:golden_rail_on", rules = rail_rules_long, }, - effector = { - action_on = function(pos, node) - local dir = mcl_minecarts:get_start_direction(pos) - if not dir then return end - local objs = minetest.get_objects_inside_radius(pos, 1) - for _, o in pairs(objs) do - local l = o:get_luaentity() - local v = o:get_velocity() - if l and string.sub(l.name, 1, 14) == "mcl_minecarts:" - and v and vector.equals(v, vector.zero()) - then - mcl_minecarts:set_velocity(l, dir) - end - end -end, - }, }, drop = "mcl_minecarts:golden_rail", }, From 40b62eea1d021ddfa1a78142d8975022c0b29041 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 31 Mar 2024 10:21:07 +0000 Subject: [PATCH 046/195] Reorganize --- mods/CORE/mcl_util/init.lua | 12 + mods/ENTITIES/mcl_minecarts/functions.lua | 121 ++++++++ mods/ENTITIES/mcl_minecarts/mod.conf | 2 +- mods/ENTITIES/mcl_minecarts/rails.lua | 268 ++++++++++++---- .../ENTITIES/mcl_minecarts/rails/standard.lua | 285 ------------------ 5 files changed, 350 insertions(+), 338 deletions(-) delete mode 100644 mods/ENTITIES/mcl_minecarts/rails/standard.lua diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index d7c2667ce..44927ddd8 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -780,3 +780,15 @@ function mcl_util.remove_entity(luaentity) luaentity.object:remove() end +local function table_merge(base, overlay) + for k,v in pairs(overlay) do + if type(base[k]) == "table" then + table_merge(base[k], v) + else + base[k] = v + end + end + return base +end +mcl_util.table_merge = table_merge + diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 8c025ae93..985fadb46 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -1,4 +1,6 @@ local vector = vector +local mod = mcl_minecarts +local table_merge = mcl_util.table_merge function mcl_minecarts:get_sign(z) if z == 0 then @@ -67,6 +69,125 @@ function mcl_minecarts:name_from_dir(dir, vertical) end +local north = vector.new( 0, 0, 1); local N = 1 +local south = vector.new( 0, 0,-1); local S = 2 -- Note: S is overwritten below with the translator +local east = vector.new( 1, 0, 0); local E = 4 +local west = vector.new(-1, 0, 0); local W = 8 + +-- Share. Consider moving this to some shared location +mod.north = north +mod.south = south +mod.east = east +mod.west = west + +local CONNECTIONS = { north, south, east, west } +local HORIZONTAL_STANDARD_RULES = { + [N] = { "", 0, mask = N, score = 1 }, + [S] = { "", 0, mask = S, score = 1 }, + [N+S] = { "", 0, mask = N+S, score = 2 }, + + [E] = { "", 1, mask = E, score = 1 }, + [W] = { "", 1, mask = W, score = 1 }, + [E+W] = { "", 1, mask = E+W, score = 2 }, +} + +local HORIZONTAL_CURVES_RULES = { + [N+E] = { "_corner", 3, name = "ne corner", mask = N+E, score = 3 }, + [N+W] = { "_corner", 2, name = "nw corner", mask = N+W, score = 3 }, + [S+E] = { "_corner", 0, name = "se corner", mask = S+E, score = 3 }, + [S+W] = { "_corner", 1, name = "sw corner", mask = S+W, score = 3 }, + + [N+E+W] = { "_tee_off", 3, mask = N+E+W, score = 4 }, + [S+E+W] = { "_tee_off", 1, mask = S+E+W, score = 4 }, + [N+S+E] = { "_tee_off", 0, mask = N+S+E, score = 4 }, + [N+S+W] = { "_tee_off", 2, mask = N+S+W, score = 4 }, + + [N+S+E+W] = { "_cross", 0, mask = N+S+E+W, score = 5 }, +} + +table_merge(HORIZONTAL_CURVES_RULES, HORIZONTAL_STANDARD_RULES) +local HORIZONTAL_RULES_BY_RAIL_GROUP = { + [1] = HORIZONTAL_STANDARD_RULES, + [2] = HORIZONTAL_CURVES_RULES, +} + +local function check_connection_rule(pos, connections, rule) + -- All bits in the mask must be set for the connection to be possible + if bit.band(rule.mask,connections) ~= rule.mask then + --print("Mask mismatch ("..tostring(rule.mask)..","..tostring(connections)..")") + return false + end + + -- If there is an allow filter, that mush also return true + if rule.allow and rule.allow(rule, connections, pos) then + return false + end + + return true +end +mod.check_connection_rules = check_connection_rules + +local function update_rail_connections(pos, update_neighbors) + local node = minetest.get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + if not nodedef._mcl_minecarts then + minetest.log("warning", "attemting to rail connect "..node.name) + return + end + + -- Get the mappings to use + local rules = HORIZONTAL_RULES_BY_RAIL_GROUP[nodedef.groups.rail] + if nodedef._mcl_minecarts and nodedef._mcl_minecarts.connection_rules then -- Custom connection rules + rules = nodedef._mcl_minecarts.connection_rules + end + if not rules then return end + + -- Horizontal rules, Check for rails on each neighbor + local connections = 0 + for i,dir in ipairs(CONNECTIONS) do + local neighbor = vector.add(pos, dir) + local node = minetest.get_node(neighbor) + local nodedef = minetest.registered_nodes[node.name] + + -- TODO: modify to only allow connections to the ends of rails (direction rules) + if (nodedef.groups or {}).rail and nodedef._mcl_minecarts and nodedef._mcl_minecarts.get_next_dir then + local diff = vector.direction(neighbor, pos) + local next_dir = nodedef._mcl_minecarts.get_next_dir(neighbor, diff, node) + if next_dir == diff then + connections = connections + bit.lshift(1,i - 1) + end + end + end + + -- Select the best allowed connection + local rule = nil + local score = 0 + for k,r in pairs(rules) do + if check_connection_rule(pos, connections, r) then + if r.score > score then + --print("Best rule so far is "..dump(r)) + score = r.score + rule = r + end + end + end + if not rule then return end + + -- Apply the mapping + local new_name = nodedef._mcl_minecarts.base_name..rule[1] + if new_name ~= node.name or node.param2 ~= rule[2] then + print("swapping "..node.name.." for "..new_name..","..tostring(rule[2]).." at "..tostring(pos)) + node.name = new_name + node.param2 = rule[2] + minetest.swap_node(pos, node) + end + + if rule.after then + rule.after(rule, pos, connections) + end +end +mod.update_rail_connections = update_rail_connections + --[[ An array of (u,v,w) positions to check. Actual direction is u * dir + v * right + w * up ]] diff --git a/mods/ENTITIES/mcl_minecarts/mod.conf b/mods/ENTITIES/mcl_minecarts/mod.conf index b810c2b6a..73148ebc3 100644 --- a/mods/ENTITIES/mcl_minecarts/mod.conf +++ b/mods/ENTITIES/mcl_minecarts/mod.conf @@ -1,5 +1,5 @@ name = mcl_minecarts author = Krock description = Minecarts are vehicles to move players quickly on rails. -depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs +depends = mcl_title, mcl_explosions, mcl_core, mcl_util, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs optional_depends = doc_identifier, mcl_wip diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index ec076cbc8..cc84390eb 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -6,6 +6,21 @@ mod.RAIL_GROUPS = { CURVES = 2, } local S = minetest.get_translator(modname) +local table_merge = mcl_util.table_merge +local check_connection_rules = mod.check_connection_rules +local update_rail_connections = mod.update_rail_connections +local north = mod.north +local south = mod.south +local east = mod.east +local west = mod.west + +-- Setup shared text +local railuse = S( + "Place them on the ground to build your railway, the rails will automatically connect to each other and will".. + " turn into curves, T-junctions, crossings and slopes as needed." +) +mod.text = mod.text or {} +mod.text.railuse = railuse local function drop_railcarts(pos) -- Scan for minecarts in this pos and force them to execute their "floating" check. @@ -44,12 +59,6 @@ local RAIL_DEFAULT_GROUPS = { transport=1 } -local function table_merge(base, overlay) - for k,v in pairs(overlay) do - base[k] = v - end -end - -- Template rail function local function register_rail(itemstring, tiles, def_extras, creative) local groups = table.copy(RAIL_DEFAULT_GROUPS) @@ -68,6 +77,44 @@ local function register_rail(itemstring, tiles, def_extras, creative) table_merge(ndef, def_extras) minetest.register_node(itemstring, ndef) end +local BASE_DEF = { + _doc_items_usagehelp = railuse, + groups = { + rail = 1, + }, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-8/16, -8/16, -8/16, 8/16, -7/16, 8/15} + } + }, + paramtype = "light", + paramtype2 = "facedir", +} +table_merge(BASE_DEF, RAIL_DEFAULTS) -- Merge together old rail values +table_merge(BASE_DEF.groups, RAIL_DEFAULT_GROUPS) + +local SLOPED_RAIL_DEF = table.copy(BASE_DEF) +table_merge(SLOPED_RAIL_DEF,{ + drawtype = "mesh", + mesh = "sloped_track.obj", + collision_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.0, 0.5 }, + { -0.5, 0.0, 0.0, 0.5, 0.5, 0.5 } + } + }, + selection_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.0, 0.5 }, + { -0.5, 0.0, 0.0, 0.5, 0.5, 0.5 } + } + } +}) + local function register_rail_v2(itemstring, def) assert(def.tiles) @@ -75,29 +122,15 @@ local function register_rail_v2(itemstring, def) local craft = def.craft def.craft = nil - -- Build rail groups - local groups = table.copy(RAIL_DEFAULT_GROUPS) - if def.groups then table_merge(groups, def.groups) end - def.groups = groups - - -- Build the node definition - local ndef = { - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - {-8/16, -8/16, -8/16, 8/16, -7/16, 8/15} - } - } - } - table_merge(ndef, RAIL_DEFAULTS) - table_merge(ndef, def) + -- Merge together the definition with the base definition + local ndef = table.copy(def) + table_merge(ndef, BASE_DEF) -- Add sensible defaults if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end - print("registering rail "..itemstring.." with definition: "..dump(ndef)) + --print("registering rail "..itemstring.." with definition: "..dump(ndef)) -- Make registrations minetest.register_node(itemstring, ndef) @@ -105,6 +138,146 @@ local function register_rail_v2(itemstring, def) end mod.register_rail = register_rail_v2 +local function rail_dir_straight(pos, dir, node) + if node.param2 == 0 or node.param2 == 2 then + if vector.equals(dir, north) then + return north + else + return south + end + else + if vector.equals(dir,east) then + return east + else + return west + end + end +end +local function rail_dir_curve(pos, dir, node) + if node.param2 == 0 then + -- South and East + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return east end + if vector.equals(dir, west) then return south end + if vector.equals(dir, east) then return east end + elseif node.param2 == 1 then + -- South and West + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return west end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return south end + elseif node.param2 == 2 then + -- North and West + if vector.equals(dir, south) then return west end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return north end + elseif node.param2 == 3 then + -- North and East + if vector.equals(dir, south) then return east end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return north end + if vector.equals(dir, east) then return east end + end +end + +local function rail_dir_cross(pos, dir, node) + -- Always continue in the same direction. No direction changes allowed + return dir +end + +-- Now get the translator after we have finished using S for other things +local S = minetest.get_translator(modname) +mod.text = mod.text or {} +mod.text.railuse = railuse +local BASE_DEF = { + description = S("New Rail"), -- Temporary name to make debugging easier + _tt_help = S("Track for minecarts"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), + groups = { + rail = mod.RAIL_GROUPS.CURVES, + }, + after_place_node = function(pos, placer, itemstack, pointed_thing) + update_rail_connections(pos, true) + end, +} +local function register_curves_rail(base_name, tiles, def) + def = def or {} + local base_def = table.copy(BASE_DEF) + table_merge(base_def,{ + _mcl_minecarts = { base_name = base_name }, + drop = base_name, + }) + table_merge(base_def, def) + + -- Register the base node + mod.register_rail(base_name, table_merge(table.copy(base_def),{ + tiles = { tiles[1] }, + _mcl_minecarts = { + get_next_dir = rail_dir_straight + } + })) + BASE_DEF.craft = nil + + -- Corner variants + mod.register_rail(base_name.."_corner", table_merge(table.copy(base_def),{ + tiles = { tiles[2] }, + _mcl_minecarts = { + get_next_dir = rail_dir_curve, + }, + groups = { + not_in_creative_inventory = 1, + }, + })) + + -- Tee variants + mod.register_rail(base_name.."_tee_off", table_merge(table.copy(base_def),{ + tiles = { tiles[3] }, + groups = { + not_in_creative_inventory = 1, + }, + mesecons = { + effector = { + action_on = function(pos, node) + local new_node = {name = base_name.."_tee_on", param2 = node.param2} + minetest.swap_node(pos, new_node) + end, + rules = mesecon.rules.alldirs, + } + } + })) + mod.register_rail(base_name.."_tee_on", table_merge(table.copy(base_def),{ + tiles = { tiles[4] }, + groups = { + not_in_creative_inventory = 1, + }, + mesecons = { + effector = { + action_off = function(pos, node) + local new_node = {name = base_name.."_tee_off", param2 = node.param2} + minetest.swap_node(pos, new_node) + end, + rules = mesecon.rules.alldirs, + } + } + })) + mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ + description = S("Sloped Rail"), -- Temporary name to make debugging easier + _mcl_minecarts = { + get_next_dir = rail_dir_cross, + }, + tiles = { tiles[1] }, + })) + + -- Cross variant + mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{ + tiles = { tiles[5] }, + groups = { + not_in_creative_inventory = 1, + }, + })) +end +mod.register_curves_rail = register_curves_rail local function register_rail_sloped(itemstring, def) assert(def.tiles) @@ -115,32 +288,14 @@ local function register_rail_sloped(itemstring, def) def.groups = groups -- Build the node definition - local ndef = table.copy(RAIL_DEFAULTS) - table_merge(ndef,{ - drawtype = "mesh", - mesh = "sloped_track.obj", - collision_box = { - type = "fixed", - fixed = { - { -0.5, -0.5, -0.5, 0.5, 0.0, 0.5 }, - { -0.5, 0.0, 0.0, 0.5, 0.5, 0.5 } - } - }, - selection_box = { - type = "fixed", - fixed = { - { -0.5, -0.5, -0.5, 0.5, 0.0, 0.5 }, - { -0.5, 0.0, 0.0, 0.5, 0.5, 0.5 } - } - } - }) + local ndef = table.copy(SLOPED_RAIL_DEF) table_merge(ndef, def) -- Add sensible defaults if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end - print("registering sloped rail "..itemstring.." with definition: "..dump(ndef)) + --print("registering sloped rail "..itemstring.." with definition: "..dump(ndef)) -- Make registrations minetest.register_node(itemstring, ndef) @@ -148,14 +303,6 @@ local function register_rail_sloped(itemstring, def) end mod.register_rail_sloped = register_rail_sloped --- Setup shared text -local railuse = S( - "Place them on the ground to build your railway, the rails will automatically connect to each other and will".. - " turn into curves, T-junctions, crossings and slopes as needed." -) -mod.text = mod.text or {} -mod.text.railuse = railuse - -- Register rails dofile(modpath.."/rails/standard.lua") @@ -189,6 +336,23 @@ register_rail("mcl_minecarts:rail", _doc_items_usagehelp = railuse, } ) +mod.register_curves_rail("mcl_minecarts:rail_v2", { + "default_rail.png", + "default_rail_curved.png", + "default_rail_t_junction.png", + "default_rail_t_junction_on.png", + "default_rail_crossing.png" +},{ + craft = { + output = "mcl_minecarts:rail_v2 16", + recipe = { + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + } + }, +}) + -- Powered rail (off = brake mode) register_rail("mcl_minecarts:golden_rail", diff --git a/mods/ENTITIES/mcl_minecarts/rails/standard.lua b/mods/ENTITIES/mcl_minecarts/rails/standard.lua deleted file mode 100644 index db757bb6d..000000000 --- a/mods/ENTITIES/mcl_minecarts/rails/standard.lua +++ /dev/null @@ -1,285 +0,0 @@ -local modname = minetest.get_current_modname() -local modpath = minetest.get_modpath(modname) -local mod = mcl_minecarts - --- This is a candidate for adding to mcl_util -local function table_merge(base, overlay) - for k,v in pairs(overlay) do - if type(base[k]) == "table" then - table_merge(base[k], v) - else - base[k] = v - end - end - return base -end - -local north = vector.new( 0, 0, 1); local N = 1 -local south = vector.new( 0, 0,-1); local S = 2 -- Note: S is overwritten below with the translator -local east = vector.new( 1, 0, 0); local E = 4 -local west = vector.new(-1, 0, 0); local W = 8 - -local CONNECTIONS = { north, south, east, west } -local HORIZONTAL_STANDARD_RULES = { - [N] = { "", 0, mask = N, score = 1 }, - [S] = { "", 0, mask = S, score = 1 }, - [N+S] = { "", 0, mask = N+S, score = 2 }, - - [E] = { "", 1, mask = E, score = 1 }, - [W] = { "", 1, mask = W, score = 1 }, - [E+W] = { "", 1, mask = E+W, score = 2 }, -} - -local HORIZONTAL_CURVES_RULES = { - [N+E] = { "_corner", 3, name = "ne corner", mask = N+E, score = 3 }, - [N+W] = { "_corner", 2, name = "nw corner", mask = N+W, score = 3 }, - [S+E] = { "_corner", 0, name = "se corner", mask = S+E, score = 3 }, - [S+W] = { "_corner", 1, name = "sw corner", mask = S+W, score = 3 }, - - [N+E+W] = { "_tee_off", 3, mask = N+E+W, score = 4 }, - [S+E+W] = { "_tee_off", 1, mask = S+E+W, score = 4 }, - [N+S+E] = { "_tee_off", 0, mask = N+S+E, score = 4 }, - [N+S+W] = { "_tee_off", 2, mask = N+S+W, score = 4 }, - - [N+S+E+W] = { "_cross", 0, mask = N+S+E+W, score = 5 }, -} - -table_merge(HORIZONTAL_CURVES_RULES, HORIZONTAL_STANDARD_RULES) -local HORIZONTAL_RULES_BY_RAIL_GROUP = { - [1] = HORIZONTAL_STANDARD_RULES, - [2] = HORIZONTAL_CURVES_RULES, -} - -local function check_connection_rule(pos, connections, rule) - -- All bits in the mask must be set for the connection to be possible - if bit.band(rule.mask,connections) ~= rule.mask then - --print("Mask mismatch ("..tostring(rule.mask)..","..tostring(connections)..")") - return false - end - - -- If there is an allow filter, that mush also return true - if rule.allow and rule.allow(rule, connections, pos) then - return false - end - - return true -end - -local function update_rail_connections(pos, update_neighbors) - local node = minetest.get_node(pos) - local nodedef = minetest.registered_nodes[node.name] - if not nodedef._mcl_minecarts then - minetest.log("warning", "attemting to rail connect "..node.name) - return - end - - -- Get the mappings to use - local rules = HORIZONTAL_RULES_BY_RAIL_GROUP[nodedef.groups.rail] - if nodedef._mcl_minecarts and nodedef._mcl_minecarts.connection_rules then -- Custom connection rules - rules = nodedef._mcl_minecarts.connection_rules - end - if not rules then return end - - -- Horizontal rules, Check for rails on each neighbor - local connections = 0 - for i,dir in ipairs(CONNECTIONS) do - local neighbor = vector.add(pos, dir) - local node = minetest.get_node(neighbor) - local nodedef = minetest.registered_nodes[node.name] - - -- TODO: modify to only allow connections to the ends of rails (direction rules) - if (nodedef.groups or {}).rail and nodedef._mcl_minecarts and nodedef._mcl_minecarts.get_next_dir then - local diff = vector.direction(neighbor, pos) - local next_dir = nodedef._mcl_minecarts.get_next_dir(neighbor, diff, node) - if next_dir == diff then - connections = connections + bit.lshift(1,i - 1) - end - end - end - - -- Select the best allowed connection - local rule = nil - local score = 0 - for k,r in pairs(rules) do - if check_connection_rule(pos, connections, r) then - if r.score > score then - --print("Best rule so far is "..dump(r)) - score = r.score - rule = r - end - end - end - if not rule then return end - - -- Apply the mapping - local new_name = nodedef._mcl_minecarts.base_name..rule[1] - if new_name ~= node.name or node.param2 ~= rule[2] then - print("swapping "..node.name.." for "..new_name..","..tostring(rule[2]).." at "..tostring(pos)) - node.name = new_name - node.param2 = rule[2] - minetest.swap_node(pos, node) - end - - if rule.after then - rule.after(rule, pos, connections) - end -end -mod.update_rail_connections = update_rail_connections - -local function rail_dir_straight(pos, dir, node) - if node.param2 == 0 or node.param2 == 2 then - if vector.equals(dir, north) then - return north - else - return south - end - else - if vector.equals(dir,east) then - return east - else - return west - end - end -end -local function rail_dir_curve(pos, dir, node) - if node.param2 == 0 then - -- South and East - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return east end - if vector.equals(dir, west) then return south end - if vector.equals(dir, east) then return east end - elseif node.param2 == 1 then - -- South and West - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return west end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return south end - elseif node.param2 == 2 then - -- North and West - if vector.equals(dir, south) then return west end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return north end - elseif node.param2 == 3 then - -- North and East - if vector.equals(dir, south) then return east end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return north end - if vector.equals(dir, east) then return east end - end -end - -local function rail_dir_cross(pos, dir, node) - -- Always continue in the same direction. No direction changes allowed - return dir -end --- Now get the translator after we have finished using S for other things -local S = minetest.get_translator(modname) -local BASE_DEF = { - description = S("New Rail"), -- Temporary name to make debugging easier - _tt_help = S("Track for minecarts"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), - _doc_items_usagehelp = mod.text.railuse, - groups = { - rail = mod.RAIL_GROUPS.CURVES, - }, - paramtype = "light", - paramtype2 = "facedir", - after_place_node = function(pos, placer, itemstack, pointed_thing) - update_rail_connections(pos, true) - end, -} -local function register_curves_rail(base_name, tiles, def) - def = def or {} - local base_def = table.copy(BASE_DEF) - table_merge(base_def,{ - _mcl_minecarts = { base_name = base_name }, - drop = base_name, - }) - table_merge(base_def, def) - - -- Register the base node - mod.register_rail(base_name, table_merge(table.copy(base_def),{ - tiles = { tiles[1] }, - _mcl_minecarts = { - get_next_dir = rail_dir_straight - } - })) - BASE_DEF.craft = nil - - -- Corner variants - mod.register_rail(base_name.."_corner", table_merge(table.copy(base_def),{ - tiles = { tiles[2] }, - _mcl_minecarts = { - get_next_dir = rail_dir_curve, - }, - groups = { - not_in_creative_inventory = 1, - }, - })) - - -- Tee variants - mod.register_rail(base_name.."_tee_off", table_merge(table.copy(base_def),{ - tiles = { tiles[3] }, - groups = { - not_in_creative_inventory = 1, - }, - mesecons = { - effector = { - action_on = function(pos, node) - local new_node = {name = base_name.."_tee_on", param2 = node.param2} - minetest.swap_node(pos, new_node) - end, - rules = mesecon.rules.alldirs, - } - } - })) - mod.register_rail(base_name.."_tee_on", table_merge(table.copy(base_def),{ - tiles = { tiles[4] }, - groups = { - not_in_creative_inventory = 1, - }, - mesecons = { - effector = { - action_off = function(pos, node) - local new_node = {name = base_name.."_tee_off", param2 = node.param2} - minetest.swap_node(pos, new_node) - end, - rules = mesecon.rules.alldirs, - } - } - })) - mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ - description = S("Sloped Rail"), -- Temporary name to make debugging easier - _mcl_minecarts = { - get_next_dir = rail_dir_cross, - }, - tiles = { tiles[1] }, - })) - - -- Cross variant - mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{ - tiles = { tiles[5] }, - groups = { - not_in_creative_inventory = 1, - }, - })) -end -mod.register_curves_rail = register_curves_rail -register_curves_rail("mcl_minecarts:rail_v2", { - "default_rail.png", - "default_rail_curved.png", - "default_rail_t_junction.png", - "default_rail_t_junction_on.png", - "default_rail_crossing.png" -},{ - craft = { - output = "mcl_minecarts:rail_v2 16", - recipe = { - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - } - }, -}) - From be18edf7c64cce66f4155696d79c78637e735c19 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 31 Mar 2024 10:41:00 +0000 Subject: [PATCH 047/195] Update all rail types to new version --- mods/ENTITIES/mcl_minecarts/rails.lua | 343 +++++++++++++------------- 1 file changed, 172 insertions(+), 171 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index cc84390eb..e4770f39b 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -201,6 +201,25 @@ local BASE_DEF = { update_rail_connections(pos, true) end, } +local function register_straight_rail(base_name, tiles, def) + def = def or {} + local base_def = table.copy(BASE_DEF) + table_merge(base_def,{ + _mcl_minecarts = { base_name = base_name }, + drop = base_name, + }) + table_merge(base_def, def) + + -- Register the base node + mod.register_rail(base_name, table_merge(table.copy(base_def),{ + tiles = { tiles[1] }, + _mcl_minecarts = { + get_next_dir = rail_dir_straight + } + })) + BASE_DEF.craft = nil +end +mod.register_straight_rail = register_straight_rail local function register_curves_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) @@ -303,9 +322,6 @@ local function register_rail_sloped(itemstring, def) end mod.register_rail_sloped = register_rail_sloped --- Register rails -dofile(modpath.."/rails/standard.lua") - -- Redstone rules local rail_rules_long = {{x=-1, y= 0, z= 0, spread=true}, @@ -327,15 +343,6 @@ local rail_rules_long = local rail_rules_short = mesecon.rules.pplate -- Normal rail -register_rail("mcl_minecarts:rail", - {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"}, - { - description = S("Rail"), - _tt_help = S("Track for minecarts"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), - _doc_items_usagehelp = railuse, - } -) mod.register_curves_rail("mcl_minecarts:rail_v2", { "default_rail.png", "default_rail_curved.png", @@ -343,6 +350,10 @@ mod.register_curves_rail("mcl_minecarts:rail_v2", { "default_rail_t_junction_on.png", "default_rail_crossing.png" },{ + description = S("Rail"), + _tt_help = S("Track for minecarts"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), + _doc_items_usagehelp = railuse, craft = { output = "mcl_minecarts:rail_v2 16", recipe = { @@ -352,188 +363,178 @@ mod.register_curves_rail("mcl_minecarts:rail_v2", { } }, }) - +register_rail("mcl_minecarts:rail", {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"}, {}, false ) -- deprecated -- Powered rail (off = brake mode) -register_rail("mcl_minecarts:golden_rail", - {"mcl_minecarts_rail_golden.png", "mcl_minecarts_rail_golden_curved.png", "mcl_minecarts_rail_golden_t_junction.png", "mcl_minecarts_rail_golden_crossing.png"}, - { - description = S("Powered Rail"), - _tt_help = S("Track for minecarts").."\n"..S("Speed up when powered, slow down when not powered"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts."), - _doc_items_usagehelp = railuse .. "\n" .. S("Without redstone power, the rail will brake minecarts. To make this rail accelerate".. - " minecarts, power it with redstone power."), - _rail_acceleration = -3, - mesecons = { - conductor = { - state = mesecon.state.off, - offstate = "mcl_minecarts:golden_rail", - onstate = "mcl_minecarts:golden_rail_on", - rules = rail_rules_long, - }, +mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_golden.png" },{ + description = S("Powered Rail"), + _tt_help = S("Track for minecarts").."\n"..S("Speed up when powered, slow down when not powered"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts."), + _doc_items_usagehelp = railuse .. "\n" .. S("Without redstone power, the rail will brake minecarts. To make this rail accelerate".. + " minecarts, power it with redstone power."), + _doc_items_create_entry = false, + _rail_acceleration = -3, + _max_acceleration_velocity = 8, + mesecons = { + conductor = { + state = mesecon.state.off, + offstate = "mcl_minecarts:golden_rail_v2", + onstate = "mcl_minecarts:golden_rail_v2_on", + rules = rail_rules_long, }, + }, + drop = "mcl_minecarts:golden_rail_v2", + craft = { + output = "mcl_minecarts:golden_rail_v2 6", + recipe = { + {"mcl_core:gold_ingot", "", "mcl_core:gold_ingot"}, + {"mcl_core:gold_ingot", "mcl_core:stick", "mcl_core:gold_ingot"}, + {"mcl_core:gold_ingot", "mesecons:redstone", "mcl_core:gold_ingot"}, + } } -) +}) +register_rail("mcl_minecarts:golden_rail", {"mcl_minecarts_rail_golden.png", "mcl_minecarts_rail_golden_curved.png", "mcl_minecarts_rail_golden_t_junction.png", "mcl_minecarts_rail_golden_crossing.png"}, {}, false ) -- deprecated -- Powered rail (on = acceleration mode) -register_rail("mcl_minecarts:golden_rail_on", - {"mcl_minecarts_rail_golden_powered.png", "mcl_minecarts_rail_golden_curved_powered.png", "mcl_minecarts_rail_golden_t_junction_powered.png", "mcl_minecarts_rail_golden_crossing_powered.png"}, - { - _doc_items_create_entry = false, - _rail_acceleration = 4, - _max_acceleration_velocity = 8, - mesecons = { - conductor = { - state = mesecon.state.on, - offstate = "mcl_minecarts:golden_rail", - onstate = "mcl_minecarts:golden_rail_on", - rules = rail_rules_long, - }, - }, - drop = "mcl_minecarts:golden_rail", +mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_rail_golden_powered.png" },{ + _doc_items_create_entry = false, + _rail_acceleration = 4, + _max_acceleration_velocity = 8, + groups = { + not_in_creative_inventory = 1, }, - false -) + mesecons = { + conductor = { + state = mesecon.state.on, + offstate = "mcl_minecarts:golden_rail_v2", + onstate = "mcl_minecarts:golden_rail_v2_on", + rules = rail_rules_long, + }, + }, + drop = "mcl_minecarts:golden_rail_v2", +}) +register_rail("mcl_minecarts:golden_rail_on", {"mcl_minecarts_rail_golden_powered.png", "mcl_minecarts_rail_golden_curved_powered.png", "mcl_minecarts_rail_golden_t_junction_powered.png", "mcl_minecarts_rail_golden_crossing_powered.png"}, { }, false ) -- deprecated -- Activator rail (off) -register_rail("mcl_minecarts:activator_rail", - {"mcl_minecarts_rail_activator.png", "mcl_minecarts_rail_activator_curved.png", "mcl_minecarts_rail_activator_t_junction.png", "mcl_minecarts_rail_activator_crossing.png"}, - { - description = S("Activator Rail"), - _tt_help = S("Track for minecarts").."\n"..S("Activates minecarts when powered"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts."), - _doc_items_usagehelp = railuse .. "\n" .. S("To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail."), - mesecons = { - conductor = { - state = mesecon.state.off, - offstate = "mcl_minecarts:activator_rail", - onstate = "mcl_minecarts:activator_rail_on", - rules = rail_rules_long, - - }, +mod.register_straight_rail("mcl_minecarts:activator_rail_v2", {"mcl_minecarts_rail_activator.png"},{ + description = S("Activator Rail"), + _tt_help = S("Track for minecarts").."\n"..S("Activates minecarts when powered"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts."), + _doc_items_usagehelp = railuse .. "\n" .. S("To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail."), + mesecons = { + conductor = { + state = mesecon.state.off, + offstate = "mcl_minecarts:activator_rail_v2", + onstate = "mcl_minecarts:activator_rail_v2_on", + rules = rail_rules_long, }, - } -) + }, + craft = { + output = "mcl_minecarts:activator_rail_v2 6", + recipe = { + {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mesecons_torch:mesecon_torch_on", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, + } + }, +}) +register_rail("mcl_minecarts:activator_rail", {"mcl_minecarts_rail_activator.png", "mcl_minecarts_rail_activator_curved.png", "mcl_minecarts_rail_activator_t_junction.png", "mcl_minecarts_rail_activator_crossing.png"}, {} ) -- deprecated -- Activator rail (on) -register_rail("mcl_minecarts:activator_rail_on", - {"mcl_minecarts_rail_activator_powered.png", "mcl_minecarts_rail_activator_curved_powered.png", "mcl_minecarts_rail_activator_t_junction_powered.png", "mcl_minecarts_rail_activator_crossing_powered.png"}, - { - _doc_items_create_entry = false, - mesecons = { - conductor = { - state = mesecon.state.on, - offstate = "mcl_minecarts:activator_rail", - onstate = "mcl_minecarts:activator_rail_on", - rules = rail_rules_long, - }, - effector = { - -- Activate minecarts - action_on = function(pos, node) - local pos2 = { x = pos.x, y =pos.y + 1, z = pos.z } - local objs = minetest.get_objects_inside_radius(pos2, 1) - for _, o in pairs(objs) do - local l = o:get_luaentity() - if l and string.sub(l.name, 1, 14) == "mcl_minecarts:" and l.on_activate_by_rail then - l:on_activate_by_rail() - end - end - end, - }, - - }, - _mcl_minecarts_on_enter = function(pos, cart) - if cart.on_activate_by_rail then - cart:on_activate_by_rail() - end - end, - drop = "mcl_minecarts:activator_rail", +mod.register_straight_rail("mcl_minecarts:activator_rail_v2_on", {"mcl_minecarts_rail_activator_powered.png"},{ + _doc_items_create_entry = false, + groups = { + not_in_creative_inventory = 1, }, - false -) + mesecons = { + conductor = { + state = mesecon.state.on, + offstate = "mcl_minecarts:activator_rail_v2", + onstate = "mcl_minecarts:activator_rail_v2_on", + rules = rail_rules_long, + }, + effector = { + -- Activate minecarts + action_on = function(pos, node) + local pos2 = { x = pos.x, y =pos.y + 1, z = pos.z } + local objs = minetest.get_objects_inside_radius(pos2, 1) + for _, o in pairs(objs) do + local l = o:get_luaentity() + if l and string.sub(l.name, 1, 14) == "mcl_minecarts:" and l.on_activate_by_rail then + l:on_activate_by_rail() + end + end + end, + }, + + }, + _mcl_minecarts_on_enter = function(pos, cart) + if cart.on_activate_by_rail then + cart:on_activate_by_rail() + end + end, + drop = "mcl_minecarts:activator_rail_v2", +}) +register_rail("mcl_minecarts:activator_rail_on", {"mcl_minecarts_rail_activator_powered.png", "mcl_minecarts_rail_activator_curved_powered.png", "mcl_minecarts_rail_activator_t_junction_powered.png", "mcl_minecarts_rail_activator_crossing_powered.png"}, { }, false ) -- deprecated -- Detector rail (off) -register_rail("mcl_minecarts:detector_rail", - {"mcl_minecarts_rail_detector.png", "mcl_minecarts_rail_detector_curved.png", "mcl_minecarts_rail_detector_t_junction.png", "mcl_minecarts_rail_detector_crossing.png"}, - { - description = S("Detector Rail"), - _tt_help = S("Track for minecarts").."\n"..S("Emits redstone power when a minecart is detected"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms."), - _doc_items_usagehelp = railuse .. "\n" .. S("To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail."), - mesecons = { - receptor = { - state = mesecon.state.off, - rules = rail_rules_short, - }, +mod.register_straight_rail("mcl_minecarts:detector_rail_v2",{"mcl_minecarts_rail_detector.png"},{ + description = S("Detector Rail"), + _tt_help = S("Track for minecarts").."\n"..S("Emits redstone power when a minecart is detected"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms."), + _doc_items_usagehelp = railuse .. "\n" .. S("To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail."), + mesecons = { + receptor = { + state = mesecon.state.off, + rules = rail_rules_short, }, - _mcl_minecarts_on_enter = function(pos, cart) - local node = minetest.get_node(pos) + }, + _mcl_minecarts_on_enter = function(pos, cart) + local node = minetest.get_node(pos) - local newnode = { - name = "mcl_minecarts:detector_rail_on", - param2 = node.param2 - } - minetest.swap_node( pos, newnode ) - mesecon.receptor_on(pos) - end, + local newnode = { + name = "mcl_minecarts:detector_rail_v2_on", + param2 = node.param2 + } + minetest.swap_node( pos, newnode ) + mesecon.receptor_on(pos) + end, + craft = { + output = "mcl_minecarts:detector_rail_v2 6", + recipe = { + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mesecons_pressureplates:pressure_plate_stone_off", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mesecons:redstone", "mcl_core:iron_ingot"}, + } } -) +}) +register_rail("mcl_minecarts:detector_rail", {"mcl_minecarts_rail_detector.png", "mcl_minecarts_rail_detector_curved.png", "mcl_minecarts_rail_detector_t_junction.png", "mcl_minecarts_rail_detector_crossing.png"}, {} ) -- deprecated -- Detector rail (on) -register_rail("mcl_minecarts:detector_rail_on", - {"mcl_minecarts_rail_detector_powered.png", "mcl_minecarts_rail_detector_curved_powered.png", "mcl_minecarts_rail_detector_t_junction_powered.png", "mcl_minecarts_rail_detector_crossing_powered.png"}, - { - _doc_items_create_entry = false, - mesecons = { - receptor = { - state = mesecon.state.on, - rules = rail_rules_short, - }, - }, - _mcl_minecarts_on_leave = function(pos, cart) - local node = minetest.get_node(pos) - - local newnode = { - name = "mcl_minecarts:detector_rail", - param2 = node.param2 - } - minetest.swap_node( pos, newnode ) - mesecon.receptor_off(pos) - end, - drop = "mcl_minecarts:detector_rail", +mod.register_straight_rail("mcl_minecarts:detector_rail_v2_on",{"mcl_minecarts_rail_detector_powered.png"},{ + groups = { + not_in_creative_inventory = 1, }, - false -) + _doc_items_create_entry = false, + mesecons = { + receptor = { + state = mesecon.state.on, + rules = rail_rules_short, + }, + }, + _mcl_minecarts_on_leave = function(pos, cart) + local node = minetest.get_node(pos) - --- Crafting -minetest.register_craft({ - output = "mcl_minecarts:golden_rail 6", - recipe = { - {"mcl_core:gold_ingot", "", "mcl_core:gold_ingot"}, - {"mcl_core:gold_ingot", "mcl_core:stick", "mcl_core:gold_ingot"}, - {"mcl_core:gold_ingot", "mesecons:redstone", "mcl_core:gold_ingot"}, - } + local newnode = { + name = "mcl_minecarts:detector_rail", + param2 = node.param2 + } + minetest.swap_node( pos, newnode ) + mesecon.receptor_off(pos) + end, + drop = "mcl_minecarts:detector_rail_v2", }) - -minetest.register_craft({ - output = "mcl_minecarts:activator_rail 6", - recipe = { - {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mesecons_torch:mesecon_torch_on", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, - } -}) - -minetest.register_craft({ - output = "mcl_minecarts:detector_rail 6", - recipe = { - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mesecons_pressureplates:pressure_plate_stone_off", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mesecons:redstone", "mcl_core:iron_ingot"}, - } -}) - +register_rail("mcl_minecarts:detector_rail_on", {"mcl_minecarts_rail_detector_powered.png", "mcl_minecarts_rail_detector_curved_powered.png", "mcl_minecarts_rail_detector_t_junction_powered.png", "mcl_minecarts_rail_detector_crossing_powered.png"}, { }, false ) -- deprecated -- Aliases if minetest.get_modpath("doc") then From 279733e7116d43d0c5040958baed99ea99c5749c Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 31 Mar 2024 11:13:50 +0000 Subject: [PATCH 048/195] Fix mcl_util.table_merge where a standard value overwrites a table, fix base definition usage, implement behavior difference when there is a solid block after a straight piece of track (this will eventually allow minecarts to fly off the end of the track) --- mods/CORE/mcl_util/init.lua | 2 +- mods/ENTITIES/mcl_minecarts/functions.lua | 2 +- mods/ENTITIES/mcl_minecarts/rails.lua | 135 ++++++++++++++-------- 3 files changed, 86 insertions(+), 53 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 44927ddd8..69472315c 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -782,7 +782,7 @@ function mcl_util.remove_entity(luaentity) end local function table_merge(base, overlay) for k,v in pairs(overlay) do - if type(base[k]) == "table" then + if type(base[k]) == "table" and type(v) == "table" then table_merge(base[k], v) else base[k] = v diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 985fadb46..f8adf8b7f 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -149,7 +149,7 @@ local function update_rail_connections(pos, update_neighbors) local node = minetest.get_node(neighbor) local nodedef = minetest.registered_nodes[node.name] - -- TODO: modify to only allow connections to the ends of rails (direction rules) + -- Only allow connections to the open ends of rails, as decribed by get_next_dir if (nodedef.groups or {}).rail and nodedef._mcl_minecarts and nodedef._mcl_minecarts.get_next_dir then local diff = vector.direction(neighbor, pos) local next_dir = nodedef._mcl_minecarts.get_next_dir(neighbor, diff, node) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index e4770f39b..8ecf0c8ec 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -77,12 +77,20 @@ local function register_rail(itemstring, tiles, def_extras, creative) table_merge(ndef, def_extras) minetest.register_node(itemstring, ndef) end + +-- Now get the translator after we have finished using S for other things +mod.text = mod.text or {} +mod.text.railuse = railuse local BASE_DEF = { + description = S("New Rail"), -- Temporary name to make debugging easier + _tt_help = S("Track for minecarts"), _doc_items_usagehelp = railuse, - groups = { - rail = 1, - }, + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), + after_place_node = function(pos, placer, itemstack, pointed_thing) + update_rail_connections(pos, true) + end, drawtype = "nodebox", + groups = RAIL_DEFAULT_GROUPS, node_box = { type = "fixed", fixed = { @@ -93,7 +101,6 @@ local BASE_DEF = { paramtype2 = "facedir", } table_merge(BASE_DEF, RAIL_DEFAULTS) -- Merge together old rail values -table_merge(BASE_DEF.groups, RAIL_DEFAULT_GROUPS) local SLOPED_RAIL_DEF = table.copy(BASE_DEF) table_merge(SLOPED_RAIL_DEF,{ @@ -115,22 +122,18 @@ table_merge(SLOPED_RAIL_DEF,{ } }) -local function register_rail_v2(itemstring, def) - assert(def.tiles) +local function register_rail_v2(itemstring, ndef) + assert(ndef.tiles) -- Extract out the craft recipe - local craft = def.craft - def.craft = nil - - -- Merge together the definition with the base definition - local ndef = table.copy(def) - table_merge(ndef, BASE_DEF) + local craft = ndef.craft + ndef.craft = nil -- Add sensible defaults if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end - --print("registering rail "..itemstring.." with definition: "..dump(ndef)) + print("registering rail "..itemstring.." with definition: "..dump(ndef)) -- Make registrations minetest.register_node(itemstring, ndef) @@ -139,18 +142,34 @@ end mod.register_rail = register_rail_v2 local function rail_dir_straight(pos, dir, node) - if node.param2 == 0 or node.param2 == 2 then - if vector.equals(dir, north) then - return north + local function inside(pos,dir,node) + if node.param2 == 0 or node.param2 == 2 then + if vector.equals(dir, north) then + return north + else + return south + end else - return south + if vector.equals(dir,east) then + return east + else + return west + end end + end + + local raw_dir = inside(pos, dir, node) + + -- Handle reversing if there is a solid block in the next position + -- Only do this for straight tracks + local next_pos = vector.add(pos, raw_dir) + local next_node = minetest.get_node(next_pos) + local node_def = minetest.registered_nodes[next_node.name] + if node_def and node_def.groups and node_def.groups.solid then + -- Reverse the direction without giving -0 members + return vector.direction(next_pos, pos) else - if vector.equals(dir,east) then - return east - else - return west - end + return raw_dir end end local function rail_dir_curve(pos, dir, node) @@ -181,50 +200,61 @@ local function rail_dir_curve(pos, dir, node) end end +local function rail_dir_tee(pos, dir, node) + -- TODO: implement + return north +end + local function rail_dir_cross(pos, dir, node) -- Always continue in the same direction. No direction changes allowed return dir end --- Now get the translator after we have finished using S for other things -local S = minetest.get_translator(modname) -mod.text = mod.text or {} -mod.text.railuse = railuse -local BASE_DEF = { - description = S("New Rail"), -- Temporary name to make debugging easier - _tt_help = S("Track for minecarts"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), - groups = { - rail = mod.RAIL_GROUPS.CURVES, - }, - after_place_node = function(pos, placer, itemstack, pointed_thing) - update_rail_connections(pos, true) - end, -} local function register_straight_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) table_merge(base_def,{ + tiles = { tiles[1] }, _mcl_minecarts = { base_name = base_name }, drop = base_name, + groups = { + rail = mod.RAIL_GROUPS.STRANDARD, + }, + _mcl_minecarts = { + base_name = base_name, + get_next_dir = rail_dir_straight + }, }) table_merge(base_def, def) -- Register the base node - mod.register_rail(base_name, table_merge(table.copy(base_def),{ - tiles = { tiles[1] }, + mod.register_rail(base_name, base_def) + base_def.craft = false + table_merge(base_def,{ + groups = { + not_in_creative_inventory = 1, + }, + }) + + -- Sloped variant + mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ + description = S("Sloped Rail"), -- Temporary name to make debugging easier _mcl_minecarts = { - get_next_dir = rail_dir_straight - } + get_next_dir = rail_dir_cross, + }, + tiles = { tiles[1] }, })) - BASE_DEF.craft = nil end mod.register_straight_rail = register_straight_rail + local function register_curves_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) table_merge(base_def,{ _mcl_minecarts = { base_name = base_name }, + groups = { + rail = mod.RAIL_GROUPS.CURVES + }, drop = base_name, }) table_merge(base_def, def) @@ -236,7 +266,14 @@ local function register_curves_rail(base_name, tiles, def) get_next_dir = rail_dir_straight } })) - BASE_DEF.craft = nil + + -- Update for other variants + base_def.craft = nil + table_merge(base_def, { + groups = { + not_in_creative_inventory = 1 + } + }) -- Corner variants mod.register_rail(base_name.."_corner", table_merge(table.copy(base_def),{ @@ -244,9 +281,6 @@ local function register_curves_rail(base_name, tiles, def) _mcl_minecarts = { get_next_dir = rail_dir_curve, }, - groups = { - not_in_creative_inventory = 1, - }, })) -- Tee variants @@ -267,8 +301,8 @@ local function register_curves_rail(base_name, tiles, def) })) mod.register_rail(base_name.."_tee_on", table_merge(table.copy(base_def),{ tiles = { tiles[4] }, - groups = { - not_in_creative_inventory = 1, + _mcl_minecarts = { + get_next_dir = rail_dir_tee, }, mesecons = { effector = { @@ -280,6 +314,8 @@ local function register_curves_rail(base_name, tiles, def) } } })) + + -- Sloped variant mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ description = S("Sloped Rail"), -- Temporary name to make debugging easier _mcl_minecarts = { @@ -291,9 +327,6 @@ local function register_curves_rail(base_name, tiles, def) -- Cross variant mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{ tiles = { tiles[5] }, - groups = { - not_in_creative_inventory = 1, - }, })) end mod.register_curves_rail = register_curves_rail From 48dbe975167bb209a56acb8807c7bc67c216bc61 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 31 Mar 2024 11:49:55 +0000 Subject: [PATCH 049/195] Re-enable rule for powering rail from underneath, have stairs block minecart movement, fix crash when lightning strikes a minecart --- mods/ENTITIES/mcl_minecarts/init.lua | 2 +- mods/ENTITIES/mcl_minecarts/rails.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 4b25c2b0f..becec34ca 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -695,7 +695,7 @@ local function register_entity(entity_id, def) -- Drop items local drop = def.drop - if not minetest.is_creative_enabled(killer:get_player_name()) then + if not killer or not minetest.is_creative_enabled(killer:get_player_name()) then for d=1, #drop do minetest.add_item(self.object:get_pos(), drop[d]) end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 8ecf0c8ec..df39fb314 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -165,7 +165,7 @@ local function rail_dir_straight(pos, dir, node) local next_pos = vector.add(pos, raw_dir) local next_node = minetest.get_node(next_pos) local node_def = minetest.registered_nodes[next_node.name] - if node_def and node_def.groups and node_def.groups.solid then + if node_def and node_def.groups and ( node_def.groups.solid or node_def.groups.stair ) then -- Reverse the direction without giving -0 members return vector.direction(next_pos, pos) else @@ -359,7 +359,7 @@ mod.register_rail_sloped = register_rail_sloped local rail_rules_long = {{x=-1, y= 0, z= 0, spread=true}, {x= 1, y= 0, z= 0, spread=true}, --- {x= 0, y=-1, z= 0, spread=true}, + {x= 0, y=-1, z= 0, spread=true}, {x= 0, y= 1, z= 0, spread=true}, {x= 0, y= 0, z=-1, spread=true}, {x= 0, y= 0, z= 1, spread=true}, From e02906e46f274b2864fcfc354ca99db0bc5c9530 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 31 Mar 2024 20:20:34 +0000 Subject: [PATCH 050/195] Get sloped connections working correctly --- mods/ENTITIES/mcl_minecarts/functions.lua | 79 +++++++++++++++---- .../mcl_minecarts/models/sloped_track.obj | 8 +- mods/ENTITIES/mcl_minecarts/rails.lua | 51 ++++++++---- 3 files changed, 102 insertions(+), 36 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index f8adf8b7f..8265d1139 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -10,6 +10,12 @@ function mcl_minecarts:get_sign(z) end end +function get_path(base, first, ...) + if not first then return base end + if not base then return end + return get_path(base[first], ...) +end + function mcl_minecarts:velocity_to_dir(v) if math.abs(v.x) > math.abs(v.z) then return vector.new( @@ -82,13 +88,13 @@ mod.west = west local CONNECTIONS = { north, south, east, west } local HORIZONTAL_STANDARD_RULES = { - [N] = { "", 0, mask = N, score = 1 }, - [S] = { "", 0, mask = S, score = 1 }, - [N+S] = { "", 0, mask = N+S, score = 2 }, + [N] = { "", 0, mask = N, score = 1, can_slope = true }, + [S] = { "", 0, mask = S, score = 1, can_slope = true }, + [N+S] = { "", 0, mask = N+S, score = 2, can_slope = true }, - [E] = { "", 1, mask = E, score = 1 }, - [W] = { "", 1, mask = W, score = 1 }, - [E+W] = { "", 1, mask = E+W, score = 2 }, + [E] = { "", 1, mask = E, score = 1, can_slope = true }, + [W] = { "", 1, mask = W, score = 1, can_slope = true }, + [E+W] = { "", 1, mask = E+W, score = 2, can_slope = true }, } local HORIZONTAL_CURVES_RULES = { @@ -127,6 +133,26 @@ local function check_connection_rule(pos, connections, rule) end mod.check_connection_rules = check_connection_rules +local function make_sloped_if_straight(pos, dir) + local node = minetest.get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + + local param2 = 0 + if dir == east then + param2 = 3 + elseif dir == west then + param2 = 1 + elseif dir == north then + param2 = 2 + elseif dir == south then + param2 = 0 + end + + if get_path( nodedef, "_mcl_minecarts", "railtype" ) == "straight" then + minetest.swap_node(pos, {name = nodedef._mcl_minecarts.base_name .. "_sloped", param2 = param2}) + end +end + local function update_rail_connections(pos, update_neighbors) local node = minetest.get_node(pos) local nodedef = minetest.registered_nodes[node.name] @@ -150,13 +176,17 @@ local function update_rail_connections(pos, update_neighbors) local nodedef = minetest.registered_nodes[node.name] -- Only allow connections to the open ends of rails, as decribed by get_next_dir - if (nodedef.groups or {}).rail and nodedef._mcl_minecarts and nodedef._mcl_minecarts.get_next_dir then + if get_path(nodedef, "groups", "rail") and get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) then + --if nodedef and (nodedef.groups or {}).rail and nodedef._mcl_minecarts and nodedef._mcl_minecarts.get_next_dir then local diff = vector.direction(neighbor, pos) local next_dir = nodedef._mcl_minecarts.get_next_dir(neighbor, diff, node) if next_dir == diff then connections = connections + bit.lshift(1,i - 1) end end + + -- Check for rasing rails to slopes + make_sloped_if_straight( vector.offset(neighbor, 0, -1, 0), dir ) end -- Select the best allowed connection @@ -171,20 +201,35 @@ local function update_rail_connections(pos, update_neighbors) end end end - if not rule then return end + if rule then - -- Apply the mapping - local new_name = nodedef._mcl_minecarts.base_name..rule[1] - if new_name ~= node.name or node.param2 ~= rule[2] then - print("swapping "..node.name.." for "..new_name..","..tostring(rule[2]).." at "..tostring(pos)) - node.name = new_name - node.param2 = rule[2] - minetest.swap_node(pos, node) + -- Apply the mapping + local new_name = nodedef._mcl_minecarts.base_name..rule[1] + if new_name ~= node.name or node.param2 ~= rule[2] then + print("swapping "..node.name.." for "..new_name..","..tostring(rule[2]).." at "..tostring(pos)) + node.name = new_name + node.param2 = rule[2] + minetest.swap_node(pos, node) + end + + if rule.after then + rule.after(rule, pos, connections) + end end - if rule.after then - rule.after(rule, pos, connections) + local node_def = minetest.registered_nodes[node.name] + if get_path(node_def, "_mcl_minecarts", "can_slope") then + for _,dir in ipairs(CONNECTIONS) do + if mcl_minecarts:is_rail(vector.offset(pos,dir.x,1,dir.z)) then + local rev_dir = vector.direction(dir,vector.new(0,0,0)) + print("try to make slope at "..tostring(pos)) + make_sloped_if_straight(pos, rev_dir) + end + end + else + print(node.name.." can't slope") end + end mod.update_rail_connections = update_rail_connections diff --git a/mods/ENTITIES/mcl_minecarts/models/sloped_track.obj b/mods/ENTITIES/mcl_minecarts/models/sloped_track.obj index 86edb4b58..b5c106278 100644 --- a/mods/ENTITIES/mcl_minecarts/models/sloped_track.obj +++ b/mods/ENTITIES/mcl_minecarts/models/sloped_track.obj @@ -1,10 +1,10 @@ # hand-made Wavefront .OBJ file for sloped rail mtllib mcl_minecarts_rail.mtl o sloped_rail.001 -v -0.500000 -0.500000 -0.500000 -v -0.500000 0.500000 0.500000 -v 0.500000 0.500000 0.500000 -v 0.500000 -0.500000 -0.500000 +v -0.500000 -0.437500 -0.500000 +v -0.500000 0.562500 0.500000 +v 0.500000 0.562500 0.500000 +v 0.500000 -0.437500 -0.500000 vt 1.000000 0.000000 vt 1.000000 1.000000 vt 0.000000 1.000000 diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index df39fb314..ba02717cd 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -98,7 +98,7 @@ local BASE_DEF = { } }, paramtype = "light", - paramtype2 = "facedir", + paramtype2 = "4dir", } table_merge(BASE_DEF, RAIL_DEFAULTS) -- Merge together old rail values @@ -213,27 +213,32 @@ end local function register_straight_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) - table_merge(base_def,{ + local sloped_def = table.copy(SLOPED_RAIL_DEF) + local add = { tiles = { tiles[1] }, - _mcl_minecarts = { base_name = base_name }, drop = base_name, groups = { - rail = mod.RAIL_GROUPS.STRANDARD, + rail = mod.RAIL_GROUPS.STANDARD, }, _mcl_minecarts = { base_name = base_name, - get_next_dir = rail_dir_straight + get_next_dir = rail_dir_straight, + can_slope = true, }, - }) - table_merge(base_def, def) + } + table_merge(base_def, add); table_merge(sloped_def, add) + table_merge(base_def, def); table_merge(sloped_def, def) -- Register the base node mod.register_rail(base_name, base_def) - base_def.craft = false + base_def.craft = nil; sloped_def.craft = nil table_merge(base_def,{ groups = { not_in_creative_inventory = 1, }, + _mcl_minecarts = { + railtype = "straight", + }, }) -- Sloped variant @@ -243,6 +248,9 @@ local function register_straight_rail(base_name, tiles, def) get_next_dir = rail_dir_cross, }, tiles = { tiles[1] }, + _mcl_minecarts = { + railtype = "sloped", + }, })) end mod.register_straight_rail = register_straight_rail @@ -250,21 +258,25 @@ mod.register_straight_rail = register_straight_rail local function register_curves_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) - table_merge(base_def,{ + local sloped_def = table.copy(SLOPED_RAIL_DEF) + local add = { _mcl_minecarts = { base_name = base_name }, groups = { rail = mod.RAIL_GROUPS.CURVES }, drop = base_name, - }) - table_merge(base_def, def) + } + table_merge(base_def, add); table_merge(sloped_def, add) + table_merge(base_def, def); table_merge(sloped_def, def) -- Register the base node mod.register_rail(base_name, table_merge(table.copy(base_def),{ tiles = { tiles[1] }, _mcl_minecarts = { - get_next_dir = rail_dir_straight - } + get_next_dir = rail_dir_straight, + railtype = "straight", + can_slope = true, + }, })) -- Update for other variants @@ -280,6 +292,7 @@ local function register_curves_rail(base_name, tiles, def) tiles = { tiles[2] }, _mcl_minecarts = { get_next_dir = rail_dir_curve, + railtype = "corner", }, })) @@ -297,12 +310,16 @@ local function register_curves_rail(base_name, tiles, def) end, rules = mesecon.rules.alldirs, } - } + }, + _mcl_minecarts = { + railtype = "corner", + }, })) mod.register_rail(base_name.."_tee_on", table_merge(table.copy(base_def),{ tiles = { tiles[4] }, _mcl_minecarts = { get_next_dir = rail_dir_tee, + railtype = "tee", }, mesecons = { effector = { @@ -316,10 +333,11 @@ local function register_curves_rail(base_name, tiles, def) })) -- Sloped variant - mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ + mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(sloped_def),{ description = S("Sloped Rail"), -- Temporary name to make debugging easier _mcl_minecarts = { get_next_dir = rail_dir_cross, + railtype = "tee", }, tiles = { tiles[1] }, })) @@ -327,6 +345,9 @@ local function register_curves_rail(base_name, tiles, def) -- Cross variant mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{ tiles = { tiles[5] }, + _mcl_minecarts = { + railtype = "cross", + }, })) end mod.register_curves_rail = register_curves_rail From 794401526698c7d59478d5667be9f1e84cffec08 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 1 Apr 2024 06:12:59 +0000 Subject: [PATCH 051/195] Fix more rail connection bugs --- mods/ENTITIES/mcl_minecarts/functions.lua | 77 ++++++++++++++--------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 8265d1139..63f3df191 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -2,6 +2,34 @@ local vector = vector local mod = mcl_minecarts local table_merge = mcl_util.table_merge +function get_path(base, first, ...) + if not first then return base end + if not base then return end + return get_path(base[first], ...) +end + +local function force_get_node(pos) + local node = minetest.get_node(pos) + if node.name ~= "ignore" then return node end + + local vm = minetest.get_voxel_manip() + local emin, emax = vm:read_from_map(pos, pos) + local area = VoxelArea:new{ + MinEdge = emin, + MaxEdge = emax, + } + local data = vm:get_data() + local param_data = vm:get_light_data() + local param2_data = vm:get_param2_data() + + local vi = area:indexp(pos) + return { + name = minetest.get_name_from_content_id(data[vi]), + param = param_data[vi], + param2 = param2_data[vi] + } +end + function mcl_minecarts:get_sign(z) if z == 0 then return 0 @@ -10,12 +38,6 @@ function mcl_minecarts:get_sign(z) end end -function get_path(base, first, ...) - if not first then return base end - if not base then return end - return get_path(base[first], ...) -end - function mcl_minecarts:velocity_to_dir(v) if math.abs(v.x) > math.abs(v.z) then return vector.new( @@ -33,25 +55,15 @@ function mcl_minecarts:velocity_to_dir(v) end function mcl_minecarts:is_rail(pos, railtype) - local node = minetest.get_node(pos).name - if node == "ignore" then - local vm = minetest.get_voxel_manip() - local emin, emax = vm:read_from_map(pos, pos) - local area = VoxelArea:new{ - MinEdge = emin, - MaxEdge = emax, - } - local data = vm:get_data() - local vi = area:indexp(pos) - node = minetest.get_name_from_content_id(data[vi]) - end - if minetest.get_item_group(node, "rail") == 0 then + local node_name = force_get_node(pos).name + + if minetest.get_item_group(node_name, "rail") == 0 then return false end if not railtype then return true end - return minetest.get_item_group(node, "connect_to_raillike") == railtype + return minetest.get_item_group(node_name, "connect_to_raillike") == railtype end --[[ @@ -177,10 +189,9 @@ local function update_rail_connections(pos, update_neighbors) -- Only allow connections to the open ends of rails, as decribed by get_next_dir if get_path(nodedef, "groups", "rail") and get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) then - --if nodedef and (nodedef.groups or {}).rail and nodedef._mcl_minecarts and nodedef._mcl_minecarts.get_next_dir then - local diff = vector.direction(neighbor, pos) - local next_dir = nodedef._mcl_minecarts.get_next_dir(neighbor, diff, node) - if next_dir == diff then + local rev_dir = vector.direction(dir,vector.new(0,0,0)) + --local next_dir = nodedef._mcl_minecarts.get_next_dir(neighbor, rev_dir, node) + if mcl_minecarts:is_connection(neighbor, rev_dir) then connections = connections + bit.lshift(1,i - 1) end end @@ -220,14 +231,12 @@ local function update_rail_connections(pos, update_neighbors) local node_def = minetest.registered_nodes[node.name] if get_path(node_def, "_mcl_minecarts", "can_slope") then for _,dir in ipairs(CONNECTIONS) do - if mcl_minecarts:is_rail(vector.offset(pos,dir.x,1,dir.z)) then - local rev_dir = vector.direction(dir,vector.new(0,0,0)) - print("try to make slope at "..tostring(pos)) + local higher_rail_pos = vector.offset(pos,dir.x,1,dir.z) + local rev_dir = vector.direction(dir,vector.new(0,0,0)) + if mcl_minecarts:is_rail(higher_rail_pos) and mcl_minecarts:is_connection(higher_rail_pos, rev_dir) then make_sloped_if_straight(pos, rev_dir) end end - else - print(node.name.." can't slope") end end @@ -273,6 +282,16 @@ local diagonal_convert = { sw = south, } +function mcl_minecarts:is_connection(pos, dir) + local node = force_get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + + local get_next_dir = get_path(nodedef, "_mcl_minecarts", "get_next_dir") + if not get_next_dir then return end + + return get_next_dir(pos, dir, node) == dir +end + function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) local pos = vector.round(pos_) From fdf8161d23010fb2f5fa8a74a1006ee8bebe25c5 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 3 Apr 2024 21:33:48 +0000 Subject: [PATCH 052/195] Add immortal item entity support, add legacy rail conversion that uses immortal item drops for corners/tees/crosses that are no longer possible --- mods/ENTITIES/mcl_item_entity/init.lua | 4 +- mods/ENTITIES/mcl_minecarts/functions.lua | 49 +++++++++++++++-------- mods/ENTITIES/mcl_minecarts/rails.lua | 49 ++++++++++++++++++++++- 3 files changed, 83 insertions(+), 19 deletions(-) diff --git a/mods/ENTITIES/mcl_item_entity/init.lua b/mods/ENTITIES/mcl_item_entity/init.lua index 7f5056617..2bc530397 100644 --- a/mods/ENTITIES/mcl_item_entity/init.lua +++ b/mods/ENTITIES/mcl_item_entity/init.lua @@ -845,6 +845,7 @@ minetest.register_entity(":__builtin:item", { _insta_collect = self._insta_collect, _flowing = self._flowing, _removed = self._removed, + _immortal = self._immortal, }) -- sfan5 guessed that the biggest serializable item -- entity would have a size of 65530 bytes. This has @@ -897,6 +898,7 @@ minetest.register_entity(":__builtin:item", { self._insta_collect = data._insta_collect self._flowing = data._flowing self._removed = data._removed + self._immortal = data._immortal end else self.itemstring = staticdata @@ -990,7 +992,7 @@ minetest.register_entity(":__builtin:item", { if self._collector_timer then self._collector_timer = self._collector_timer + dtime end - if time_to_live > 0 and self.age > time_to_live then + if time_to_live > 0 and ( self.age > time_to_live and not self._immortal ) then self._removed = true self.object:remove() return diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 63f3df191..5c498ab5d 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -108,6 +108,7 @@ local HORIZONTAL_STANDARD_RULES = { [W] = { "", 1, mask = W, score = 1, can_slope = true }, [E+W] = { "", 1, mask = E+W, score = 2, can_slope = true }, } +mod.HORIZONTAL_STANDARD_RULES = HORIZONTAL_STANDARD_RULES local HORIZONTAL_CURVES_RULES = { [N+E] = { "_corner", 3, name = "ne corner", mask = N+E, score = 3 }, @@ -122,8 +123,9 @@ local HORIZONTAL_CURVES_RULES = { [N+S+E+W] = { "_cross", 0, mask = N+S+E+W, score = 5 }, } - table_merge(HORIZONTAL_CURVES_RULES, HORIZONTAL_STANDARD_RULES) +mod.HORIZONTAL_CURVES_RULES = HORIZONTAL_CURVES_RULES + local HORIZONTAL_RULES_BY_RAIL_GROUP = { [1] = HORIZONTAL_STANDARD_RULES, [2] = HORIZONTAL_CURVES_RULES, @@ -165,7 +167,31 @@ local function make_sloped_if_straight(pos, dir) end end -local function update_rail_connections(pos, update_neighbors) +local function get_rail_connections(pos, opt) + local legacy = opt and opt.legacy + local ignore_neighbor_connections = opt and opt.ignore_neighbor_connections + + local connections = 0 + for i,dir in ipairs(CONNECTIONS) do + local neighbor = vector.add(pos, dir) + local node = minetest.get_node(neighbor) + local nodedef = minetest.registered_nodes[node.name] + + -- Only allow connections to the open ends of rails, as decribed by get_next_dir + if get_path(nodedef, "groups", "rail") and ( legacy or get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) ) then + local rev_dir = vector.direction(dir,vector.new(0,0,0)) + if ignore_neighbor_connections or mcl_minecarts:is_connection(neighbor, rev_dir) then + connections = connections + bit.lshift(1,i - 1) + end + end + end + return connections +end +mod.get_rail_connections = get_rail_connections + +local function update_rail_connections(pos, opt) + local ignore_neighbor_connections = opt and opt.ignore_neighbor_connections + local node = minetest.get_node(pos) local nodedef = minetest.registered_nodes[node.name] if not nodedef._mcl_minecarts then @@ -181,22 +207,11 @@ local function update_rail_connections(pos, update_neighbors) if not rules then return end -- Horizontal rules, Check for rails on each neighbor - local connections = 0 + local connections = get_rail_connections(pos, opt) + + -- Check for rasing rails to slopes for i,dir in ipairs(CONNECTIONS) do local neighbor = vector.add(pos, dir) - local node = minetest.get_node(neighbor) - local nodedef = minetest.registered_nodes[node.name] - - -- Only allow connections to the open ends of rails, as decribed by get_next_dir - if get_path(nodedef, "groups", "rail") and get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) then - local rev_dir = vector.direction(dir,vector.new(0,0,0)) - --local next_dir = nodedef._mcl_minecarts.get_next_dir(neighbor, rev_dir, node) - if mcl_minecarts:is_connection(neighbor, rev_dir) then - connections = connections + bit.lshift(1,i - 1) - end - end - - -- Check for rasing rails to slopes make_sloped_if_straight( vector.offset(neighbor, 0, -1, 0), dir ) end @@ -212,8 +227,8 @@ local function update_rail_connections(pos, update_neighbors) end end end - if rule then + if rule then -- Apply the mapping local new_name = nodedef._mcl_minecarts.base_name..rule[1] if new_name ~= node.name or node.param2 ~= rule[2] then diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index ba02717cd..0924f9502 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -87,7 +87,7 @@ local BASE_DEF = { _doc_items_usagehelp = railuse, _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), after_place_node = function(pos, placer, itemstack, pointed_thing) - update_rail_connections(pos, true) + update_rail_connections(pos) end, drawtype = "nodebox", groups = RAIL_DEFAULT_GROUPS, @@ -595,3 +595,50 @@ if minetest.get_modpath("doc") then doc.add_entry_alias("nodes", "mcl_minecarts:golden_rail", "nodes", "mcl_minecarts:golden_rail_on") end +if 0==0 then +local CURVY_RAILS_MAP = { + ["mcl_minecarts:rail"] = "mcl_minecarts:rail_v2", +} +minetest.register_lbm({ + name = "mcl_minecarts:update_legacy_curvy_rails", + nodenames = {"mcl_minecarts:rail"}, + action = function(pos, node) + node.name = CURVY_RAILS_MAP[node.name] + if node.name then + minetest.swap_node(pos, node) + mod.update_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true }) + end + end +}) +local STRAIGHT_RAILS_MAP ={ + ["mcl_minecarts:golden_rail"] = "mcl_minecarts:golden_rail_v2", + ["mcl_minecarts:golden_rail_on"] = "mcl_minecarts:golden_rail_v2_on", + ["mcl_minecarts:activator_rail"] = "mcl_minecarts_activator_rail_v2", + ["mcl_minecarts:activator_rail_on"] = "mcl_minecarts:activator_rail_v2_on", + ["mcl_minecarts:detector_rail"] = "mcl_minecarts:detector_rail_v2", + ["mcl_minecarts:detector_rail_on"] = "mcl_minecarts:detector_rail_v2_on", +} +minetest.register_lbm({ + name = "mcl_minecarts:update_legacy_straight_rails", + nodenames = {"mcl_minecarts:golden_rail"}, + action = function(pos, node) + node.name = STRAIGHT_RAILS_MAP[node.name] + if node.name then + local connections = mod.get_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true }) + if not mod.HORIZONTAL_STANDARD_RULES[connections] then + -- Drop an immortal object at this location + local item_entity = minetest.add_item(pos, ItemStack(node.name)) + if item_entity then + item_entity:get_luaentity()._immortal = true + end + + -- This is a configuration that doesn't exist in the new rail + -- Replace with a standard rail + node.name = "mcl_minecarts:rail_v2" + end + minetest.swap_node(pos, node) + mod.update_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true }) + end + end +}) +end From 140689cd7d44ab40c2be16e93fe1184460b8607f Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 3 Apr 2024 22:07:14 +0000 Subject: [PATCH 053/195] Silence unmaskable print statements --- mods/ENTITIES/mcl_minecarts/functions.lua | 2 +- mods/ENTITIES/mcl_minecarts/init.lua | 4 ++-- mods/ENTITIES/mcl_minecarts/rails.lua | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 5c498ab5d..63673b631 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -232,7 +232,7 @@ local function update_rail_connections(pos, opt) -- Apply the mapping local new_name = nodedef._mcl_minecarts.base_name..rule[1] if new_name ~= node.name or node.param2 ~= rule[2] then - print("swapping "..node.name.." for "..new_name..","..tostring(rule[2]).." at "..tostring(pos)) + --print("swapping "..node.name.." for "..new_name..","..tostring(rule[2]).." at "..tostring(pos)) node.name = new_name node.param2 = rule[2] minetest.swap_node(pos, node) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index becec34ca..2ee7f8e12 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -850,7 +850,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) -- Call placer if le._mcl_minecarts_on_place then - print("Calling on_place") + --print("Calling on_place") le._mcl_minecarts_on_place(le, placer) end @@ -1124,7 +1124,7 @@ function table_metadata(table) return { table = table, set_string = function(self, key, value) - print("set_string("..tostring(key)..", "..tostring(value)..")") + --print("set_string("..tostring(key)..", "..tostring(value)..")") self.table[key] = tostring(value) end, get_string = function(self, key) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 0924f9502..367d91970 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -133,7 +133,7 @@ local function register_rail_v2(itemstring, ndef) if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end - print("registering rail "..itemstring.." with definition: "..dump(ndef)) + --print("registering rail "..itemstring.." with definition: "..dump(ndef)) -- Make registrations minetest.register_node(itemstring, ndef) From e7605b7abd3ffebe8beaf26fe556760a8b55178a Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 4 Apr 2024 07:33:16 +0000 Subject: [PATCH 054/195] Make legacy rail update apply to all old rail types, add basic detached railcart physics with a stub to use mcl_physics when it gets merged --- mods/CORE/mcl_util/init.lua | 8 +++ mods/ENTITIES/mcl_minecarts/functions.lua | 1 - mods/ENTITIES/mcl_minecarts/init.lua | 65 +++++++++++++++++++++-- mods/ENTITIES/mcl_minecarts/mod.conf | 2 +- mods/ENTITIES/mcl_minecarts/rails.lua | 7 +-- 5 files changed, 72 insertions(+), 11 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 69472315c..5330948c6 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -792,3 +792,11 @@ local function table_merge(base, overlay) end mcl_util.table_merge = table_merge +function mcl_util.table_keys(t) + local keys = {} + for k,_ in pairs(t) do + keys[#keys + 1] = k + end + return keys +end + diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 63673b631..766412f71 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -145,7 +145,6 @@ local function check_connection_rule(pos, connections, rule) return true end -mod.check_connection_rules = check_connection_rules local function make_sloped_if_straight(pos, dir) local node = minetest.get_node(pos) diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 2ee7f8e12..137de228a 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -39,6 +39,22 @@ mcl_minecarts.on_enter_below = function(pos, cart, next_dir, node_def) end end +local function detach_minecart(self) + local staticdata = self._staticdata + print("Detaching minecart") + + staticdata.connected_at = nil + self.object:set_velocity(staticdata.dir * staticdata.velocity) +end +local function try_detach_minecart(self) + local staticdata = self._staticdata + + local node = minetest.get_node(staticdata.connected_at) + if minetest.get_item_group(node.name, "rail") == 0 then + detach_minecart(self) + end +end + --[[ Array of hooks { {u,v,w}, name } Actual position is pos + u * dir + v * right + w * up @@ -175,6 +191,9 @@ end local function calculate_acceleration(self, staticdata) local acceleration = 0 + -- Fix up movement data + staticdata.velocity = staticdata.velocity or 0 + -- Apply friction if moving if staticdata.velocity > 0 then acceleration = -friction @@ -338,6 +357,8 @@ local function do_movement_step(self, dtime) -- Enter the new node handle_cart_enter(self, pos, next_dir) + try_detach_minecart(self) + -- Handle end of track if next_dir == staticdata.dir * -1 and next_dir.y == 0 then if DEBUG then print("Stopping cart at end of track at "..tostring(pos)) end @@ -412,6 +433,40 @@ local function do_movement( self, dtime ) end end +local function do_detached_movement(self, dtime) + local staticdata = self._staticdata + + -- Apply physics + if mcl_physics then + mcl_physics.apply_entity_environmental_physics(self) + else + -- Simple physics + local friction = self.object:get_velocity() + friction.y = 0 + + local accel = vector.new(0,-9.81,0) -- gravity + accel = vector.add(accel, vector.multiply(friction,-0.9)) + self.object:set_acceleration(accel) + end + + -- Try to reconnect to rail + local pos_r = vector.round(self.object:get_pos()) + local node = minetest.get_node(pos_r) + if minetest.get_item_group(node.name, "rail") ~= 0 then + print("Reconnected railcart at "..tostring(pos_r)) + staticdata.connected_at = pos_r + staticdata.railtype = node.name + + local freebody_velocity = self.object:get_velocity() + staticdata.dir = vector.normalize(freebody_velocity) + staticdata.velocity = vector.length(freebody_velocity) + + -- Clear freebody movement + self.object:set_velocity(vector.new(0,0,0)) + self.object:set_acceleration(vector.new(0,0,0)) + end +end + local function detach_driver(self) if not self._driver then return @@ -756,6 +811,7 @@ local function register_entity(entity_id, def) end -- Fix railtype field + local pos = self.object:get_pos() if staticdata.connected_at and not staticdata.railtype then local node = minetest.get_node(vector.floor(pos)).name staticdata.railtype = minetest.get_item_group(node, "connect_to_raillike") @@ -769,8 +825,6 @@ local function register_entity(entity_id, def) staticdata.hopper_delay = staticdata.hopper_delay - dtime end - local pos, rou_pos, node = self.object:get_pos() - -- Controls local ctrl, player = nil, nil if self._driver then @@ -794,7 +848,11 @@ local function register_entity(entity_id, def) end end - do_movement(self, dtime) + if staticdata.connected_at then + do_movement(self, dtime) + else + do_detached_movement(self, dtime) + end -- TODO: move this into mcl_core:cactus _mcl_minecarts_on_enter_side -- Drop minecart if it collides with a cactus node @@ -850,7 +908,6 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) -- Call placer if le._mcl_minecarts_on_place then - --print("Calling on_place") le._mcl_minecarts_on_place(le, placer) end diff --git a/mods/ENTITIES/mcl_minecarts/mod.conf b/mods/ENTITIES/mcl_minecarts/mod.conf index 73148ebc3..994abe809 100644 --- a/mods/ENTITIES/mcl_minecarts/mod.conf +++ b/mods/ENTITIES/mcl_minecarts/mod.conf @@ -2,4 +2,4 @@ name = mcl_minecarts author = Krock description = Minecarts are vehicles to move players quickly on rails. depends = mcl_title, mcl_explosions, mcl_core, mcl_util, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs -optional_depends = doc_identifier, mcl_wip +optional_depends = doc_identifier, mcl_wip, mcl_physics diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 367d91970..a1872ab4a 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -372,7 +372,6 @@ local function register_rail_sloped(itemstring, def) -- Make registrations minetest.register_node(itemstring, ndef) - if craft then minetest.register_craft(craft) end end mod.register_rail_sloped = register_rail_sloped @@ -595,13 +594,12 @@ if minetest.get_modpath("doc") then doc.add_entry_alias("nodes", "mcl_minecarts:golden_rail", "nodes", "mcl_minecarts:golden_rail_on") end -if 0==0 then local CURVY_RAILS_MAP = { ["mcl_minecarts:rail"] = "mcl_minecarts:rail_v2", } minetest.register_lbm({ name = "mcl_minecarts:update_legacy_curvy_rails", - nodenames = {"mcl_minecarts:rail"}, + nodenames = mcl_util.table_keys(CURVY_RAILS_MAP), action = function(pos, node) node.name = CURVY_RAILS_MAP[node.name] if node.name then @@ -620,7 +618,7 @@ local STRAIGHT_RAILS_MAP ={ } minetest.register_lbm({ name = "mcl_minecarts:update_legacy_straight_rails", - nodenames = {"mcl_minecarts:golden_rail"}, + nodenames = mcl_util.table_keys(STRAIGHT_RAILS_MAP), action = function(pos, node) node.name = STRAIGHT_RAILS_MAP[node.name] if node.name then @@ -641,4 +639,3 @@ minetest.register_lbm({ end end }) -end From f490846892cd01059f758e419d7ece79d0689833 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 4 Apr 2024 18:20:14 +0000 Subject: [PATCH 055/195] Get rail reattachment (especially after jumps) working correctly --- mods/ENTITIES/mcl_minecarts/functions.lua | 25 +++++++++++++++++++++++ mods/ENTITIES/mcl_minecarts/init.lua | 9 ++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 766412f71..9f1157ad6 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -98,6 +98,31 @@ mod.south = south mod.east = east mod.west = west +--[[ + mcl_minecarts.snap_direction(dir) + + returns a valid cart direction that has the smallest angle difference to `dir' +]] +local VALID_DIRECTIONS = { + north, vector.offset(north, 0, 1, 0), vector.offset(north, 0, -1, 0), + south, vector.offset(south, 0, 1, 0), vector.offset(south, 0, -1, 0), + east, vector.offset(east, 0, 1, 0), vector.offset(east, 0, -1, 0), + west, vector.offset(west, 0, 1, 0), vector.offset(west, 0, -1, 0), +} +function mod.snap_direction(dir) + dir = vector.normalize(dir) + local best = nil + local diff = -1 + for _,d in pairs(VALID_DIRECTIONS) do + local dot = vector.dot(dir,d) + if dot > diff then + best = d + diff = dot + end + end + return best +end + local CONNECTIONS = { north, south, east, west } local HORIZONTAL_STANDARD_RULES = { [N] = { "", 0, mask = N, score = 1, can_slope = true }, diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 137de228a..426e534f1 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -41,7 +41,6 @@ end local function detach_minecart(self) local staticdata = self._staticdata - print("Detaching minecart") staticdata.connected_at = nil self.object:set_velocity(staticdata.dir * staticdata.velocity) @@ -453,13 +452,15 @@ local function do_detached_movement(self, dtime) local pos_r = vector.round(self.object:get_pos()) local node = minetest.get_node(pos_r) if minetest.get_item_group(node.name, "rail") ~= 0 then - print("Reconnected railcart at "..tostring(pos_r)) staticdata.connected_at = pos_r staticdata.railtype = node.name local freebody_velocity = self.object:get_velocity() - staticdata.dir = vector.normalize(freebody_velocity) - staticdata.velocity = vector.length(freebody_velocity) + staticdata.dir = mod:get_rail_direction(pos_r, mod.snap_direction(freebody_velocity)) + + -- Use vector projection to only keep the velocity in the new direction of movement on the rail + -- https://en.wikipedia.org/wiki/Vector_projection + staticdata.velocity = vector.dot(staticdata.dir,freebody_velocity) -- Clear freebody movement self.object:set_velocity(vector.new(0,0,0)) From b59d6d37dcb7f04bc3ef6d040ce19863d9e6118a Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 4 Apr 2024 22:08:20 +0000 Subject: [PATCH 056/195] Cleanup code, restore uphill/downhill cart movement, completely remove old rail --- mods/ENTITIES/mcl_minecarts/functions.lua | 133 ++++-------- mods/ENTITIES/mcl_minecarts/rails.lua | 249 ++++++++++------------ 2 files changed, 148 insertions(+), 234 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 9f1157ad6..2ed625e02 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -7,7 +7,6 @@ function get_path(base, first, ...) if not base then return end return get_path(base[first], ...) end - local function force_get_node(pos) local node = minetest.get_node(pos) if node.name ~= "ignore" then return node end @@ -66,31 +65,11 @@ function mcl_minecarts:is_rail(pos, railtype) return minetest.get_item_group(node_name, "connect_to_raillike") == railtype end ---[[ - Returns a string description of a direction, with optional _up/_down suffix -]] -function mcl_minecarts:name_from_dir(dir, vertical) - local res = "" - - if dir.z == 1 then res = res .. "n" end - if dir.z == -1 then res = res .. "s" end - - if dir.x == -1 then res = res .. "w" end - if dir.x == 1 then res = res .. "e" end - - if vertical then - if dir.y == 1 then res = res .. "_up" end - if dir.y == 1 then res = res .. "_down" end - end - - return res -end - - -local north = vector.new( 0, 0, 1); local N = 1 -local south = vector.new( 0, 0,-1); local S = 2 -- Note: S is overwritten below with the translator -local east = vector.new( 1, 0, 0); local E = 4 -local west = vector.new(-1, 0, 0); local W = 8 +-- Directional constants +local north = vector.new( 0, 0, 1); local N = 1 -- 4dir = 0 +local east = vector.new( 1, 0, 0); local E = 4 -- 4dir = 1 +local south = vector.new( 0, 0,-1); local S = 2 -- 4dir = 2 Note: S is overwritten below with the translator +local west = vector.new(-1, 0, 0); local W = 8 -- 4dir = 3 -- Share. Consider moving this to some shared location mod.north = north @@ -191,6 +170,16 @@ local function make_sloped_if_straight(pos, dir) end end +local function is_connection(pos, dir) + local node = force_get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + + local get_next_dir = get_path(nodedef, "_mcl_minecarts", "get_next_dir") + if not get_next_dir then return end + + return get_next_dir(pos, dir, node) == dir +end + local function get_rail_connections(pos, opt) local legacy = opt and opt.legacy local ignore_neighbor_connections = opt and opt.ignore_neighbor_connections @@ -204,7 +193,7 @@ local function get_rail_connections(pos, opt) -- Only allow connections to the open ends of rails, as decribed by get_next_dir if get_path(nodedef, "groups", "rail") and ( legacy or get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) ) then local rev_dir = vector.direction(dir,vector.new(0,0,0)) - if ignore_neighbor_connections or mcl_minecarts:is_connection(neighbor, rev_dir) then + if ignore_neighbor_connections or is_connection(neighbor, rev_dir) then connections = connections + bit.lshift(1,i - 1) end end @@ -272,7 +261,7 @@ local function update_rail_connections(pos, opt) for _,dir in ipairs(CONNECTIONS) do local higher_rail_pos = vector.offset(pos,dir.x,1,dir.z) local rev_dir = vector.direction(dir,vector.new(0,0,0)) - if mcl_minecarts:is_rail(higher_rail_pos) and mcl_minecarts:is_connection(higher_rail_pos, rev_dir) then + if mcl_minecarts:is_rail(higher_rail_pos) and is_connection(higher_rail_pos, rev_dir) then make_sloped_if_straight(pos, rev_dir) end end @@ -281,85 +270,45 @@ local function update_rail_connections(pos, opt) end mod.update_rail_connections = update_rail_connections ---[[ - An array of (u,v,w) positions to check. Actual direction is u * dir + v * right + w * up -]] -local rail_checks = { - { 1, 0, 0 }, -- forwards - { 1, 0, 1 }, -- forwards and up - { 1, 0, -1 }, -- forwards and down - - { 1, 1, 0 }, -- diagonal left - { 0, 1, 0 }, -- left - { 0, 1, 1 }, -- left and up - { 0, 1, -1 }, -- left and down - - { 1, -1, 0 }, -- diagonal right - { 0, -1, 0 }, -- right - { 0, -1, 1 }, -- right and up - { 0, -1, -1 }, -- right and down - - { -1, 0, 0 }, -- backwards -} - -local rail_checks_diagonal = { - { 1, 1, 0 }, -- forward along diagonal - { 1, 0, 0 }, -- left - { 0, 1, 0 }, -- right -} - local north = vector.new(0,0,1) local south = vector.new(0,0,-1) local east = vector.new(1,0,0) local west = vector.new(-1,0,0) --- Rotate diagonal directions 45 degrees clockwise -local diagonal_convert = { - nw = west, - ne = north, - se = east, - sw = south, -} +local function is_ahead_slope(pos, dir) + local ahead = vector.add(pos,dir) + if mcl_minecarts:is_rail(ahead) then return false end -function mcl_minecarts:is_connection(pos, dir) - local node = force_get_node(pos) - local nodedef = minetest.registered_nodes[node.name] + local below = vector.offset(ahead,0,-1,0) + if not mcl_minecarts:is_rail(below) then return false end - local get_next_dir = get_path(nodedef, "_mcl_minecarts", "get_next_dir") - if not get_next_dir then return end - - return get_next_dir(pos, dir, node) == dir + local node_name = force_get_node(below).name + return minetest.get_item_group(node_name, "rail_slope") ~= 0 end - function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) local pos = vector.round(pos_) -- Handle new track types that have track-specific direction handler local node = minetest.get_node(pos) local node_def = minetest.registered_nodes[node.name] - if node_def and node_def._mcl_minecarts and node_def._mcl_minecarts.get_next_dir then - return node_def._mcl_minecarts.get_next_dir(pos, dir, node) + local get_next_dir = get_path(node_def,"_mcl_minecarts","get_next_dir") + if not get_next_dir then return dir end + + dir = node_def._mcl_minecarts.get_next_dir(pos, dir, node) + + -- Handle going downhill + if is_ahead_slope(pos,dir) then + dir = vector.offset(dir,0,-1,0) end - -- Diagonal conversion - local checks = rail_checks - if dir.x ~= 0 and dir.z ~= 0 then - dir = diagonal_convert[ mcl_minecarts:name_from_dir(dir, false) ] - checks = rail_checks_diagonal + -- Handle reversing if there is a solid block in the next position + local next_pos = vector.add(pos, dir) + local next_node = minetest.get_node(next_pos) + local node_def = minetest.registered_nodes[next_node.name] + if node_def and node_def.groups and ( node_def.groups.solid or node_def.groups.stair ) then + -- Reverse the direction without giving -0 members + return vector.direction(next_pos, pos) + else + return dir end - - -- Calculate coordinate space - local right = vector.new( dir.z, dir.y, -dir.x) - local up = vector.new(0,1,0) - - -- Perform checks - for _,check in ipairs(checks) do - local check_dir = dir * check[1] + right * check[2] + up * check[3] - local check_pos = pos + check_dir - if mcl_minecarts:is_rail(check_pos,railtype) then - return check_dir - end - end - - return vector.new(0,0,0) end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index a1872ab4a..8b373369f 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -1,11 +1,13 @@ local modname = minetest.get_current_modname() local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts +local S = minetest.get_translator(modname) mod.RAIL_GROUPS = { STANDARD = 1, CURVES = 2, } -local S = minetest.get_translator(modname) + +-- Inport functions and constants from elsewhere local table_merge = mcl_util.table_merge local check_connection_rules = mod.check_connection_rules local update_rail_connections = mod.update_rail_connections @@ -14,14 +16,6 @@ local south = mod.south local east = mod.east local west = mod.west --- Setup shared text -local railuse = S( - "Place them on the ground to build your railway, the rails will automatically connect to each other and will".. - " turn into curves, T-junctions, crossings and slopes as needed." -) -mod.text = mod.text or {} -mod.text.railuse = railuse - local function drop_railcarts(pos) -- Scan for minecarts in this pos and force them to execute their "floating" check. -- Normally, this will make them drop. @@ -37,60 +31,78 @@ local function drop_railcarts(pos) end end -local RAIL_DEFAULTS = { - is_ground_content = true, - paramtype = "light", - selection_box = { - type = "fixed", - fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, - }, - stack_max = 64, - sounds = mcl_sounds.node_sound_metal_defaults(), - _mcl_blast_resistance = 0.7, - _mcl_hardness = 0.7, - after_destruct = drop_railcarts, -} -local RAIL_DEFAULT_GROUPS = { - handy=1, pickaxey=1, - attached_node=1, - rail=1, - connect_to_raillike=minetest.raillike_group("rail"), - dig_by_water=0,destroy_by_lava_flow=0, - transport=1 -} - --- Template rail function -local function register_rail(itemstring, tiles, def_extras, creative) - local groups = table.copy(RAIL_DEFAULT_GROUPS) - if creative == false then - groups.not_in_creative_inventory = 1 +--- Rail direction Handleres +local function rail_dir_straight(pos, dir, node) + if node.param2 == 0 or node.param2 == 2 then + if vector.equals(dir, north) then + return north + else + return south + end + else + if vector.equals(dir,east) then + return east + else + return west + end end - local ndef = { - drawtype = "raillike", - tiles = tiles, - inventory_image = tiles[1], - wield_image = tiles[1], - groups = groups, - } - table_merge(ndef, RAIL_DEFAULTS) - ndef.walkable = false -- Old behavior - table_merge(ndef, def_extras) - minetest.register_node(itemstring, ndef) +end +local function rail_dir_sloped(pos, dir, node) + local uphill = minetest.fourdir_to_dir(node.param2) + local downhill = minetest.fourdir_to_dir((node.param2+2)%4) + local up_uphill = vector.offset(uphill,0,1,0) + + if vector.equals(dir, uphill) or vector.equals(dir, up_uphill) then + return up_uphill + else + return downhill + end +end +local function rail_dir_curve(pos, dir, node) + if node.param2 == 0 then -- north + -- South and East + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return east end + if vector.equals(dir, west) then return south end + if vector.equals(dir, east) then return east end + elseif node.param2 == 1 then -- east + -- South and West + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return west end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return south end + elseif node.param2 == 2 then + -- North and West + if vector.equals(dir, south) then return west end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return north end + elseif node.param2 == 3 then + -- North and East + if vector.equals(dir, south) then return east end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return north end + if vector.equals(dir, east) then return east end + end +end +local function rail_dir_tee(pos, dir, node) + minetest.log("warning","TODO: implement rail_dir_tee()") + return north +end +local function rail_dir_cross(pos, dir, node) + -- Always continue in the same direction. No direction changes allowed + return dir end --- Now get the translator after we have finished using S for other things +-- Setup shared text +local railuse = S( + "Place them on the ground to build your railway, the rails will automatically connect to each other and will".. + " turn into curves, T-junctions, crossings and slopes as needed." +) mod.text = mod.text or {} mod.text.railuse = railuse local BASE_DEF = { - description = S("New Rail"), -- Temporary name to make debugging easier - _tt_help = S("Track for minecarts"), - _doc_items_usagehelp = railuse, - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), - after_place_node = function(pos, placer, itemstack, pointed_thing) - update_rail_connections(pos) - end, drawtype = "nodebox", - groups = RAIL_DEFAULT_GROUPS, node_box = { type = "fixed", fixed = { @@ -99,13 +111,44 @@ local BASE_DEF = { }, paramtype = "light", paramtype2 = "4dir", + stack_max = 64, + sounds = mcl_sounds.node_sound_metal_defaults(), + is_ground_content = true, + paramtype = "light", + selection_box = { + type = "fixed", + fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, + }, + groups = { + handy=1, pickaxey=1, + attached_node=1, + rail=1, + connect_to_raillike=minetest.raillike_group("rail"), + dig_by_water=0,destroy_by_lava_flow=0, + transport=1 + }, + description = S("New Rail"), -- Temporary name to make debugging easier + _tt_help = S("Track for minecarts"), + _doc_items_usagehelp = railuse, + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), + after_place_node = function(pos, placer, itemstack, pointed_thing) + update_rail_connections(pos) + end, + after_destruct = drop_railcarts, + _mcl_minecarts = { + get_next_dir = rail_dir_straight, + }, + _mcl_blast_resistance = 0.7, + _mcl_hardness = 0.7, } -table_merge(BASE_DEF, RAIL_DEFAULTS) -- Merge together old rail values local SLOPED_RAIL_DEF = table.copy(BASE_DEF) table_merge(SLOPED_RAIL_DEF,{ drawtype = "mesh", mesh = "sloped_track.obj", + groups = { + rail_slope = 1, + }, collision_box = { type = "fixed", fixed = { @@ -119,7 +162,10 @@ table_merge(SLOPED_RAIL_DEF,{ { -0.5, -0.5, -0.5, 0.5, 0.0, 0.5 }, { -0.5, 0.0, 0.0, 0.5, 0.5, 0.5 } } - } + }, + _mcl_minecarts = { + get_next_dir = rail_dir_sloped, + }, }) local function register_rail_v2(itemstring, ndef) @@ -141,75 +187,6 @@ local function register_rail_v2(itemstring, ndef) end mod.register_rail = register_rail_v2 -local function rail_dir_straight(pos, dir, node) - local function inside(pos,dir,node) - if node.param2 == 0 or node.param2 == 2 then - if vector.equals(dir, north) then - return north - else - return south - end - else - if vector.equals(dir,east) then - return east - else - return west - end - end - end - - local raw_dir = inside(pos, dir, node) - - -- Handle reversing if there is a solid block in the next position - -- Only do this for straight tracks - local next_pos = vector.add(pos, raw_dir) - local next_node = minetest.get_node(next_pos) - local node_def = minetest.registered_nodes[next_node.name] - if node_def and node_def.groups and ( node_def.groups.solid or node_def.groups.stair ) then - -- Reverse the direction without giving -0 members - return vector.direction(next_pos, pos) - else - return raw_dir - end -end -local function rail_dir_curve(pos, dir, node) - if node.param2 == 0 then - -- South and East - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return east end - if vector.equals(dir, west) then return south end - if vector.equals(dir, east) then return east end - elseif node.param2 == 1 then - -- South and West - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return west end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return south end - elseif node.param2 == 2 then - -- North and West - if vector.equals(dir, south) then return west end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return north end - elseif node.param2 == 3 then - -- North and East - if vector.equals(dir, south) then return east end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return north end - if vector.equals(dir, east) then return east end - end -end - -local function rail_dir_tee(pos, dir, node) - -- TODO: implement - return north -end - -local function rail_dir_cross(pos, dir, node) - -- Always continue in the same direction. No direction changes allowed - return dir -end - local function register_straight_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) @@ -222,7 +199,6 @@ local function register_straight_rail(base_name, tiles, def) }, _mcl_minecarts = { base_name = base_name, - get_next_dir = rail_dir_straight, can_slope = true, }, } @@ -245,7 +221,7 @@ local function register_straight_rail(base_name, tiles, def) mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ description = S("Sloped Rail"), -- Temporary name to make debugging easier _mcl_minecarts = { - get_next_dir = rail_dir_cross, + get_next_dir = rail_dir_sloped, }, tiles = { tiles[1] }, _mcl_minecarts = { @@ -336,7 +312,7 @@ local function register_curves_rail(base_name, tiles, def) mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(sloped_def),{ description = S("Sloped Rail"), -- Temporary name to make debugging easier _mcl_minecarts = { - get_next_dir = rail_dir_cross, + get_next_dir = rail_dir_sloped, railtype = "tee", }, tiles = { tiles[1] }, @@ -346,6 +322,7 @@ local function register_curves_rail(base_name, tiles, def) mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{ tiles = { tiles[5] }, _mcl_minecarts = { + get_next_dir = rail_dir_cross, railtype = "cross", }, })) @@ -355,11 +332,6 @@ mod.register_curves_rail = register_curves_rail local function register_rail_sloped(itemstring, def) assert(def.tiles) - -- Build rail groups - local groups = table.copy(RAIL_DEFAULT_GROUPS) - if def.groups then table_merge(groups, def.groups) end - def.groups = groups - -- Build the node definition local ndef = table.copy(SLOPED_RAIL_DEF) table_merge(ndef, def) @@ -416,7 +388,6 @@ mod.register_curves_rail("mcl_minecarts:rail_v2", { } }, }) -register_rail("mcl_minecarts:rail", {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"}, {}, false ) -- deprecated -- Powered rail (off = brake mode) mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_golden.png" },{ @@ -446,7 +417,6 @@ mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_ } } }) -register_rail("mcl_minecarts:golden_rail", {"mcl_minecarts_rail_golden.png", "mcl_minecarts_rail_golden_curved.png", "mcl_minecarts_rail_golden_t_junction.png", "mcl_minecarts_rail_golden_crossing.png"}, {}, false ) -- deprecated -- Powered rail (on = acceleration mode) mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_rail_golden_powered.png" },{ @@ -466,7 +436,6 @@ mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_ra }, drop = "mcl_minecarts:golden_rail_v2", }) -register_rail("mcl_minecarts:golden_rail_on", {"mcl_minecarts_rail_golden_powered.png", "mcl_minecarts_rail_golden_curved_powered.png", "mcl_minecarts_rail_golden_t_junction_powered.png", "mcl_minecarts_rail_golden_crossing_powered.png"}, { }, false ) -- deprecated -- Activator rail (off) mod.register_straight_rail("mcl_minecarts:activator_rail_v2", {"mcl_minecarts_rail_activator.png"},{ @@ -491,7 +460,6 @@ mod.register_straight_rail("mcl_minecarts:activator_rail_v2", {"mcl_minecarts_ra } }, }) -register_rail("mcl_minecarts:activator_rail", {"mcl_minecarts_rail_activator.png", "mcl_minecarts_rail_activator_curved.png", "mcl_minecarts_rail_activator_t_junction.png", "mcl_minecarts_rail_activator_crossing.png"}, {} ) -- deprecated -- Activator rail (on) mod.register_straight_rail("mcl_minecarts:activator_rail_v2_on", {"mcl_minecarts_rail_activator_powered.png"},{ @@ -528,7 +496,6 @@ mod.register_straight_rail("mcl_minecarts:activator_rail_v2_on", {"mcl_minecarts end, drop = "mcl_minecarts:activator_rail_v2", }) -register_rail("mcl_minecarts:activator_rail_on", {"mcl_minecarts_rail_activator_powered.png", "mcl_minecarts_rail_activator_curved_powered.png", "mcl_minecarts_rail_activator_t_junction_powered.png", "mcl_minecarts_rail_activator_crossing_powered.png"}, { }, false ) -- deprecated -- Detector rail (off) mod.register_straight_rail("mcl_minecarts:detector_rail_v2",{"mcl_minecarts_rail_detector.png"},{ @@ -561,7 +528,6 @@ mod.register_straight_rail("mcl_minecarts:detector_rail_v2",{"mcl_minecarts_rail } } }) -register_rail("mcl_minecarts:detector_rail", {"mcl_minecarts_rail_detector.png", "mcl_minecarts_rail_detector_curved.png", "mcl_minecarts_rail_detector_t_junction.png", "mcl_minecarts_rail_detector_crossing.png"}, {} ) -- deprecated -- Detector rail (on) mod.register_straight_rail("mcl_minecarts:detector_rail_v2_on",{"mcl_minecarts_rail_detector_powered.png"},{ @@ -587,7 +553,6 @@ mod.register_straight_rail("mcl_minecarts:detector_rail_v2_on",{"mcl_minecarts_r end, drop = "mcl_minecarts:detector_rail_v2", }) -register_rail("mcl_minecarts:detector_rail_on", {"mcl_minecarts_rail_detector_powered.png", "mcl_minecarts_rail_detector_curved_powered.png", "mcl_minecarts_rail_detector_t_junction_powered.png", "mcl_minecarts_rail_detector_crossing_powered.png"}, { }, false ) -- deprecated -- Aliases if minetest.get_modpath("doc") then From e1b973fbf5429e5b1368a9e7125790c6a3108c3e Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 5 Apr 2024 06:39:42 +0000 Subject: [PATCH 057/195] Move cart code to its own file, more code cleanup, add aliases for old track items --- mods/ENTITIES/mcl_minecarts/carts.lua | 1368 ++++++++++++++++++++++++ mods/ENTITIES/mcl_minecarts/init.lua | 1406 +------------------------ mods/ENTITIES/mcl_minecarts/rails.lua | 6 + 3 files changed, 1375 insertions(+), 1405 deletions(-) create mode 100644 mods/ENTITIES/mcl_minecarts/carts.lua diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua new file mode 100644 index 000000000..4a3068edd --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -0,0 +1,1368 @@ +local modname = minetest.get_current_modname() +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +local LOGGING_ON = minetest.settings:get_bool("mcl_logging_minecarts", false) +local DEBUG = false +local function mcl_log(message) + if LOGGING_ON then + mcl_util.mcl_log(message, "[Minecarts]", true) + end +end + +-- Import external functions +local table_merge = mcl_util.table_merge + +-- Constants +local max_step_distance = 0.5 +local friction = 0.4 +local MINECART_MAX_HP = 4 +local PASSENGER_ATTACH_POSITION = vector.new(0, -1.75, 0) + +local function detach_minecart(self) + local staticdata = self._staticdata + + staticdata.connected_at = nil + self.object:set_velocity(staticdata.dir * staticdata.velocity) +end +local function try_detach_minecart(self) + local staticdata = self._staticdata + + local node = minetest.get_node(staticdata.connected_at) + if minetest.get_item_group(node.name, "rail") == 0 then + detach_minecart(self) + end +end + +--[[ + Array of hooks { {u,v,w}, name } + Actual position is pos + u * dir + v * right + w * up +]] +local enter_exit_checks = { + { 0, 0, 0, "" }, + { 0, 0, 1, "_above" }, + { 0, 0,-1, "_below" }, + { 0, 1, 0, "_side" }, + { 0,-1, 0, "_side" }, +} + +local function handle_cart_enter_exit(self, pos, next_dir, event) + local staticdata = self._staticdata + + local dir = staticdata.dir + local right = vector.new( dir.z, dir.y, -dir.x) + local up = vector.new(0,1,0) + for _,check in ipairs(enter_exit_checks) do + local check_pos = pos + dir * check[1] + right * check[2] + up * check[3] + local node = minetest.get_node(check_pos) + local node_def = minetest.registered_nodes[node.name] + + -- node-specific hook + local hook_name = "_mcl_minecarts_"..event..check[4] + local hook = node_def[hook_name] + if hook then hook(check_pos, self, next_dir, pos) end + + -- global minecart hook + hook = mcl_minecarts[event..check[4]] + if hook then hook(check_pos, self, next_dir, node_def) end + end + + -- Handle cart-specific behaviors + local hook = self["_mcl_minecarts_"..event] + if hook then hook(self, pos) end +end +local function handle_cart_enter(self, pos, next_dir) + handle_cart_enter_exit(self, pos, next_dir, "on_enter" ) +end +local function handle_cart_leave(self, pos, next_dir) + handle_cart_enter_exit(self, pos, next_dir, "on_leave" ) +end + +local function handle_cart_node_watches(self, dtime) + local staticdata = self._staticdata + local watches = staticdata.node_watches or {} + local new_watches = {} + for _,node_pos in ipairs(watches) do + local node = minetest.get_node(node_pos) + local node_def = minetest.registered_nodes[node.name] + local hook = node_def._mcl_minecarts_node_on_step + if hook and hook(node_pos, self, dtime) then + new_watches[#new_watches+1] = node_pos + end + end + + staticdata.node_watches = new_watches +end + +local function update_cart_orientation(self,staticdata) + -- constants + local _2_pi = math.pi * 2 + local pi = math.pi + local dir = staticdata.dir + + -- Calculate an angle from the x,z direction components + local rot_y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) + if rot_y < 0 then + rot_y = rot_y + _2_pi + end + + -- Check if the rotation is a 180 flip and don't change if so + local rot = self.object:get_rotation() + local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) + if diff < 0.001 or diff > _2_pi - 0.001 then + -- Update rotation adjust and recalculate the rotation + staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi + rot.y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) + else + rot.y = rot_y + end + + -- Forward/backwards tilt (pitch) + if dir.y < 0 then + rot.x = -0.25 * pi + elseif dir.y > 0 then + rot.x = 0.25 * pi + else + rot.x = 0 + end + + if ( staticdata.rot_adjust or 0 ) < 0.01 then + rot.x = -rot.x + end + if dir.z ~= 0 then + rot.x = -rot.x + end + + self.object:set_rotation(rot) +end + +local function direction_away_from_players(self, staticdata) + local objs = minetest.get_objects_inside_radius(self.object:get_pos(), 1.1) + for n=1,#objs do + local obj = objs[n] + local player_name = obj:get_player_name() + if player_name and player_name ~= "" and not ( self._driver and self._driver == player_name ) then + local diff = obj:get_pos() - self.object:get_pos() + + local length = vector.distance(vector.new(0,0,0),diff) + local vec = diff / length + local force = vector.dot( vec, vector.normalize(staticdata.dir) ) + + -- Check if this would push past the end of the track and don't move it it would + -- This prevents an oscillation that would otherwise occur + local dir = staticdata.dir + if force > 0 then + dir = -dir + end + if mcl_minecarts:is_rail( staticdata.connected_at + dir ) then + if force > 0.5 then + return -length * 4 + elseif force < -0.5 then + return length * 4 + end + end + end + end + + return 0 +end + +local function calculate_acceleration(self, staticdata) + local acceleration = 0 + + -- Fix up movement data + staticdata.velocity = staticdata.velocity or 0 + + -- Apply friction if moving + if staticdata.velocity > 0 then + acceleration = -friction + end + + local pos = staticdata.connected_at + local node_name = minetest.get_node(pos).name + local node_def = minetest.registered_nodes[node_name] + local max_vel = mcl_minecarts.speed_max + + if self._go_forward then + acceleration = 4 + elseif self._brake then + acceleration = -1.5 + elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then + acceleration = 0.6 + elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then + -- Standard friction + else + if node_def._rail_acceleration then + acceleration = node_def._rail_acceleration * 4 + end + end + + -- Factor in gravity after everything else + local gravity_strength = 2.45 --friction * 5 + if staticdata.dir.y < 0 then + acceleration = gravity_strength - friction + elseif staticdata.dir.y > 0 then + acceleration = -gravity_strength + friction + end + + return acceleration +end + +local function reverse_direction(self, staticdata) + -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at + local next_dir = -staticdata.dir + staticdata.connected_at = staticdata.connected_at + staticdata.dir + staticdata.distance = 1 - staticdata.distance + + -- recalculate direction + local next_dir,_ = mcl_minecarts:get_rail_direction(staticdata.connected_at, next_dir, nil, nil, staticdata.railtype) + staticdata.dir = next_dir +end + +local function do_movement_step(self, dtime) + local staticdata = self._staticdata + if not staticdata.connected_at then return 0 end + + -- Calculate timestep remaiing in this block + local x_0 = staticdata.distance or 0 + local remaining_in_block = 1 - x_0 + local a = calculate_acceleration(self, staticdata) + local v_0 = staticdata.velocity + + -- Repel minecarts + local away = direction_away_from_players(self, staticdata) + if away > 0 then + v_0 = away + elseif away < 0 then + reverse_direction(self, staticdata) + v_0 = -away + end + + if DEBUG and ( v_0 > 0 or a ~= 0 ) then + print( " cart #"..tostring(staticdata.cart_id).. + ": a="..tostring(a).. + ",v_0="..tostring(v_0).. + ",x_0="..tostring(x_0).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir).. + ",connected_at="..tostring(staticdata.connected_at).. + ",distance="..tostring(staticdata.distance) + ) + end + + -- Not moving + if a == 0 and v_0 == 0 then return 0 end + + -- Movement equation with acceleration: x_1 = x_0 + v_0 * t + 0.5 * a * t*t + local timestep + local stops_in_block = false + local inside = v_0 * v_0 + 2 * a * remaining_in_block + if inside < 0 then + -- Would stop or reverse direction inside this block, calculate time to v_1 = 0 + timestep = -v_0 / a + stops_in_block = true + elseif a ~= 0 then + -- Setting x_1 = x_0 + remaining_in_block, and solving for t gives: + timestep = ( math.sqrt( v_0 * v_0 + 2 * a * remaining_in_block) - v_0 ) / a + else + timestep = remaining_in_block / v_0 + end + + -- Truncate timestep to remaining time delta + if timestep > dtime then + timestep = dtime + end + + -- Truncate timestep to prevent v_1 from being larger that speed_max + local v_max = mcl_minecarts.speed_max + if (v_0 ~= v_max) and ( v_0 + a * timestep > v_max) then + timestep = ( v_max - v_0 ) / a + end + + -- Prevent infinite loops + if timestep <= 0 then return 0 end + + -- Calculate v_1 taking v_max into account + local v_1 = v_0 + a * timestep + if v_1 > v_max then + v_1 = v_max + elseif v_1 < friction / 5 then + v_1 = 0 + end + + -- Calculate x_1 + local x_1 = x_0 + timestep * v_0 + 0.5 * a * timestep * timestep + + -- Update position and velocity of the minecart + staticdata.velocity = v_1 + staticdata.distance = x_1 + + if DEBUG and (1==0) and ( v_0 > 0 or a ~= 0 ) then + print( "- cart #"..tostring(staticdata.cart_id).. + ": a="..tostring(a).. + ",v_0="..tostring(v_0).. + ",v_1="..tostring(v_1).. + ",x_0="..tostring(x_0).. + ",x_1="..tostring(x_1).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir).. + ",connected_at="..tostring(staticdata.connected_at).. + ",distance="..tostring(staticdata.distance) + ) + end + + + -- Entity movement + local pos = staticdata.connected_at + + -- Handle movement to next block, account for loss of precision in calculations + if x_1 >= 0.99 then + staticdata.distance = 0 + + -- Anchor at the next node + local old_pos = pos + pos = pos + staticdata.dir + staticdata.connected_at = pos + + -- Get the next direction + local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) + if DEBUG and next_dir ~= staticdata.dir then + print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) + end + + -- Leave the old node + handle_cart_leave(self, old_pos, next_dir ) + + -- Enter the new node + handle_cart_enter(self, pos, next_dir) + + try_detach_minecart(self) + + -- Handle end of track + if next_dir == staticdata.dir * -1 and next_dir.y == 0 then + if DEBUG then print("Stopping cart at end of track at "..tostring(pos)) end + staticdata.velocity = 0 + end + + -- Update cart direction + staticdata.dir = next_dir + elseif stops_in_block and v_1 < (friction/5) and a <= 0 then + -- Handle direction flip due to gravity + if DEBUG then print("Gravity flipped direction") end + + -- Velocity should be zero at this point + staticdata.velocity = 0 + + reverse_direction(self, staticdata) + + -- Intermediate movement + pos = staticdata.connected_at + staticdata.dir * staticdata.distance + else + -- Intermediate movement + pos = pos + staticdata.dir * staticdata.distance + end + + self.object:move_to(pos) + + -- Update cart orientation + update_cart_orientation(self,staticdata) + + -- Debug reporting + if DEBUG and ( v_0 > 0 or v_1 > 0 ) then + print( " cart #"..tostring(staticdata.cart_id).. + ": a="..tostring(a).. + ",v_0="..tostring(v_0).. + ",v_1="..tostring(v_1).. + ",x_0="..tostring(x_0).. + ",x_1="..tostring(x_1).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir).. + ",pos="..tostring(pos).. + ",connected_at="..tostring(staticdata.connected_at).. + ",distance="..tostring(staticdata.distance) + ) + end + + -- Report the amount of time processed + return dtime - timestep +end + +local function do_movement( self, dtime ) + local staticdata = self._staticdata + + -- Allow the carts to be delay for the rest of the world to react before moving again + if ( staticdata.delay or 0 ) > dtime then + staticdata.delay = staticdata.delay - dtime + return + else + staticdata.delay = 0 + end + + -- Break long movements at block boundaries to make it + -- it impossible to jump across gaps due to server lag + -- causing large timesteps + while dtime > 0 do + local new_dtime = do_movement_step(self, dtime) + + -- Handle node watches here in steps to prevent server lag from changing behavior + handle_cart_node_watches(self, dtime - new_dtime) + + dtime = new_dtime + end +end + +local function do_detached_movement(self, dtime) + local staticdata = self._staticdata + + -- Apply physics + if mcl_physics then + mcl_physics.apply_entity_environmental_physics(self) + else + -- Simple physics + local friction = self.object:get_velocity() + friction.y = 0 + + local accel = vector.new(0,-9.81,0) -- gravity + accel = vector.add(accel, vector.multiply(friction,-0.9)) + self.object:set_acceleration(accel) + end + + -- Try to reconnect to rail + local pos_r = vector.round(self.object:get_pos()) + local node = minetest.get_node(pos_r) + if minetest.get_item_group(node.name, "rail") ~= 0 then + staticdata.connected_at = pos_r + staticdata.railtype = node.name + + local freebody_velocity = self.object:get_velocity() + staticdata.dir = mod:get_rail_direction(pos_r, mod.snap_direction(freebody_velocity)) + + -- Use vector projection to only keep the velocity in the new direction of movement on the rail + -- https://en.wikipedia.org/wiki/Vector_projection + staticdata.velocity = vector.dot(staticdata.dir,freebody_velocity) + + -- Clear freebody movement + self.object:set_velocity(vector.new(0,0,0)) + self.object:set_acceleration(vector.new(0,0,0)) + end +end + +local function detach_driver(self) + if not self._driver then + return + end + mcl_player.player_attached[self._driver] = nil + local player = minetest.get_player_by_name(self._driver) + self._driver = nil + self._start_pos = nil + if player then + player:set_detach() + player:set_eye_offset(vector.new(0,0,0),vector.new(0,0,0)) + mcl_player.player_set_animation(player, "stand" , 30) + end +end + +local function activate_normal_minecart(self) + detach_driver(self) + + -- Detach passenger + if self._passenger then + local mob = self._passenger.object + mob:set_detach() + end +end + +local function hopper_take_item(self, dtime) + local pos = self.object:get_pos() + if not pos then return end + + if not self or self.name ~= "mcl_minecarts:hopper_minecart" then return end + + if mcl_util.check_dtime_timer(self, dtime, "hoppermc_take", 0.15) then + --minetest.log("The check timer was triggered: " .. dump(pos) .. ", name:" .. self.name) + else + --minetest.log("The check timer was not triggered") + return + end + + + local above_pos = vector.offset(pos, 0, 0.9, 0) + local objs = minetest.get_objects_inside_radius(above_pos, 1.25) + + if objs then + mcl_log("there is an itemstring. Number of objs: ".. #objs) + + for k, v in pairs(objs) do + local ent = v:get_luaentity() + + if ent and not ent._removed and ent.itemstring and ent.itemstring ~= "" then + local taken_items = false + + mcl_log("ent.name: " .. tostring(ent.name)) + mcl_log("ent pos: " .. tostring(ent.object:get_pos())) + + local inv = mcl_entity_invs.load_inv(self, 5) + if not inv then return false end + + local current_itemstack = ItemStack(ent.itemstring) + + mcl_log("inv. size: " .. self._inv_size) + if inv:room_for_item("main", current_itemstack) then + mcl_log("Room") + inv:add_item("main", current_itemstack) + ent.object:get_luaentity().itemstring = "" + ent.object:remove() + taken_items = true + else + mcl_log("no Room") + end + + if not taken_items then + local items_remaining = current_itemstack:get_count() + + -- This will take part of a floating item stack if no slot can hold the full amount + for i = 1, self._inv_size, 1 do + local stack = inv:get_stack("main", i) + + mcl_log("i: " .. tostring(i)) + mcl_log("Items remaining: " .. items_remaining) + mcl_log("Name: " .. tostring(stack:get_name())) + + if current_itemstack:get_name() == stack:get_name() then + mcl_log("We have a match. Name: " .. tostring(stack:get_name())) + + local room_for = stack:get_stack_max() - stack:get_count() + mcl_log("Room for: " .. tostring(room_for)) + + if room_for == 0 then + -- Do nothing + mcl_log("No room") + elseif room_for < items_remaining then + mcl_log("We have more items remaining than space") + + items_remaining = items_remaining - room_for + stack:set_count(stack:get_stack_max()) + inv:set_stack("main", i, stack) + taken_items = true + else + local new_stack_size = stack:get_count() + items_remaining + stack:set_count(new_stack_size) + mcl_log("We have more than enough space. Now holds: " .. new_stack_size) + + inv:set_stack("main", i, stack) + items_remaining = 0 + + ent.object:get_luaentity().itemstring = "" + ent.object:remove() + + taken_items = true + break + end + + mcl_log("Count: " .. tostring(stack:get_count())) + mcl_log("stack max: " .. tostring(stack:get_stack_max())) + --mcl_log("Is it empty: " .. stack:to_string()) + end + + if i == self._inv_size and taken_items then + mcl_log("We are on last item and still have items left. Set final stack size: " .. items_remaining) + current_itemstack:set_count(items_remaining) + --mcl_log("Itemstack2: " .. current_itemstack:to_string()) + ent.itemstring = current_itemstack:to_string() + end + end + end + + --Add in, and delete + if taken_items then + mcl_log("Saving") + mcl_entity_invs.save_inv(ent) + return taken_items + else + mcl_log("No need to save") + end + + end + end + end + + return false +end + +-- Table for item-to-entity mapping. Keys: itemstring, Values: Corresponding entity ID +local entity_mapping = {} + +local function make_staticdata( railtype, connected_at, dir ) + return { + railtype = railtype, + connected_at = connected_at, + distance = 0, + velocity = 0, + dir = vector.new(dir), + cart_id = math.random(1,1000000000), + } +end + +local DEFAULT_CART_DEF = { + initial_properties = { + physical = true, + collisionbox = {-10/16., -0.5, -10/16, 10/16, 0.25, 10/16}, + visual = "mesh", + visual_size = {x=1, y=1}, + }, + + hp_max = MINECART_MAX_HP, + + groups = { + minecart = 1, + }, + + _driver = nil, -- player who sits in and controls the minecart (only for minecart!) + _passenger = nil, -- for mobs + _start_pos = nil, -- Used to calculate distance for “On A Rail” achievement + _last_float_check = nil, -- timestamp of last time the cart was checked to be still on a rail + _boomtimer = nil, -- how many seconds are left before exploding + _blinktimer = nil, -- how many seconds are left before TNT blinking + _blink = false, -- is TNT blink texture active? + _old_pos = nil, + _staticdata = nil, +} +function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) + -- Initialize + local data = minetest.deserialize(staticdata) + if type(data) == "table" then + -- Migrate old data + if data._railtype then + data.railtype = data._railtype + data._railtype = nil + end + -- Fix up types + data.dir = vector.new(data.dir) + + -- Make sure all carts have an ID to isolate them + data.cart_id = staticdata.cart_id or math.random(1,1000000000) + + self._staticdata = data + end + + -- Activate cart if on activator rail + if self.on_activate_by_rail then + local pos = self.object:get_pos() + local node = minetest.get_node(vector.floor(pos)) + if node.name == "mcl_minecarts:activator_rail_on" then + self:on_activate_by_rail() + end + end +end +function DEFAULT_CART_DEF:add_node_watch(pos) + local staticdata = self._staticdata + local watches = staticdata.node_watches or {} + + for _,watch in ipairs(watches) do + if watch == pos then return end + end + + watches[#watches+1] = pos + staticdata.node_watches = watches +end +function DEFAULT_CART_DEF:remove_node_watch(pos) + local staticdata = self._staticdata + local watches = staticdata.node_watches or {} + + local new_watches = {} + for _,node_pos in ipairs(watches) do + if node_pos ~= pos then + new_watches[#new_watches] = node_pos + end + end + staticdata.node_watches = new_watches +end +function DEFAULT_CART_DEF:get_staticdata() + return minetest.serialize(self._staticdata or {}) +end +function DEFAULT_CART_DEF:on_step(dtime) + local staticdata = self._staticdata + if not staticdata then + staticdata = make_staticdata() + self._staticdata = staticdata + end + + -- Regen + local hp = self.object:get_hp() + if hp < MINECART_MAX_HP then + if (staticdata.regen_timer or 0) > 0.5 then + hp = hp + 1 + staticdata.regen_timer = staticdata.regen_timer - 1 + end + staticdata.regen_timer = (staticdata.regen_timer or 0) + dtime + self.object:set_hp(hp) + else + staticdata.regen_timer = nil + end + + -- Fix railtype field + local pos = self.object:get_pos() + if staticdata.connected_at and not staticdata.railtype then + local node = minetest.get_node(vector.floor(pos)).name + staticdata.railtype = minetest.get_item_group(node, "connect_to_raillike") + end + + -- Cart specific behaviors + local hook = self._mcl_minecarts_on_step + if hook then hook(self,dtime) end + + if (staticdata.hopper_delay or 0) > 0 then + staticdata.hopper_delay = staticdata.hopper_delay - dtime + end + + -- Controls + local ctrl, player = nil, nil + if self._driver then + player = minetest.get_player_by_name(self._driver) + if player then + ctrl = player:get_player_control() + -- player detach + if ctrl.sneak then + detach_driver(self) + return + end + + -- Experimental controls + self._go_forward = ctrl.up + self._brake = ctrl.down + end + + -- Give achievement when player reached a distance of 1000 nodes from the start position + if vector.distance(self._start_pos, pos) >= 1000 then + awards.unlock(self._driver, "mcl:onARail") + end + end + + if staticdata.connected_at then + do_movement(self, dtime) + else + do_detached_movement(self, dtime) + end + + -- TODO: move this into mcl_core:cactus _mcl_minecarts_on_enter_side + -- Drop minecart if it collides with a cactus node + local r = 0.6 + for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do + if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then + detach_driver(self) + local drop = self.drop + for d = 1, #drop do + minetest.add_item(pos, drop[d]) + end + self.object:remove() + return + end + end +end +function DEFAULT_CART_DEF:on_death(killer) + local staticdata = self._staticdata + detach_driver(self) + + -- Detach passenger + if self._passenger then + local mob = self._passenger.object + mob:set_detach() + end + + -- Leave nodes + if staticdata.attached_at then + handle_cart_leave(self, staticdata.attached_at, staticdata.dir ) + else + mcl_log("TODO: handle detatched minecart death") + end + + -- Drop items + local drop = self.drop + if not killer or not minetest.is_creative_enabled(killer:get_player_name()) then + for d=1, #drop do + minetest.add_item(self.object:get_pos(), drop[d]) + end + elseif killer and killer:is_player() then + local inv = killer:get_inventory() + for d=1, #drop do + if not inv:contains_item("main", drop[d]) then + inv:add_item("main", drop[d]) + end + end + end +end + +-- Place a minecart at pointed_thing +function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) + if not pointed_thing.type == "node" then + return + end + + local railpos, node + if mcl_minecarts:is_rail(pointed_thing.under) then + railpos = pointed_thing.under + node = minetest.get_node(pointed_thing.under) + elseif mcl_minecarts:is_rail(pointed_thing.above) then + railpos = pointed_thing.above + node = minetest.get_node(pointed_thing.above) + else + return + end + + local entity_id = entity_mapping[itemstack:get_name()] + local cart = minetest.add_entity(railpos, entity_id) + local railtype = minetest.get_item_group(node.name, "connect_to_raillike") + local cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0), nil, nil, railtype) + cart:set_yaw(minetest.dir_to_yaw(cart_dir)) + + -- Update static data + local le = cart:get_luaentity() + if le then + le._staticdata = make_staticdata( railtype, railpos, cart_dir ) + end + + -- Call placer + if le._mcl_minecarts_on_place then + le._mcl_minecarts_on_place(le, placer) + end + + handle_cart_enter(le, railpos) + + local pname = "" + if placer then + pname = placer:get_player_name() + end + if not minetest.is_creative_enabled(pname) then + itemstack:take_item() + end + return itemstack +end + +local function dropper_place_minecart(dropitem, pos) + local node = minetest.get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + + -- Don't try to place the minecart if pos isn't a rail + if (nodedef.groups.rail or 0) == 0 then return false end + + mcl_minecarts.place_minecart(dropitem, { + above = pos, + under = vector.offset(pos,0,-1,0) + }) + return true +end + +local function register_minecart_craftitem(itemstring, def) + local groups = { minecart = 1, transport = 1 } + if def.creative == false then + groups.not_in_creative_inventory = 1 + end + local item_def = { + stack_max = 1, + _mcl_dropper_on_drop = dropper_place_minecart, + on_place = function(itemstack, placer, pointed_thing) + if not pointed_thing.type == "node" then + return + end + + -- Call on_rightclick if the pointed node defines it + local node = minetest.get_node(pointed_thing.under) + if placer and not placer:get_player_control().sneak then + if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then + return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack + end + end + + return mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) + end, + _on_dispense = function(stack, pos, droppos, dropnode, dropdir) + -- Place minecart as entity on rail. If there's no rail, just drop it. + local placed + if minetest.get_item_group(dropnode.name, "rail") ~= 0 then + -- FIXME: This places minecarts even if the spot is already occupied + local pointed_thing = { under = droppos, above = vector.new( droppos.x, droppos.y+1, droppos.z ) } + placed = mcl_minecarts.place_minecart(stack, pointed_thing) + end + if placed == nil then + -- Drop item + minetest.add_item(droppos, stack) + end + end, + groups = groups, + } + item_def.description = def.description + item_def._tt_help = def.tt_help + item_def._doc_items_longdesc = def.longdesc + item_def._doc_items_usagehelp = def.usagehelp + item_def.inventory_image = def.icon + item_def.wield_image = def.icon + minetest.register_craftitem(itemstring, item_def) +end + +--[[ +Register a minecart +* itemstring: Itemstring of minecart item +* entity_id: ID of minecart entity +* description: Item name / description +* longdesc: Long help text +* usagehelp: Usage help text +* mesh: Minecart mesh +* textures: Minecart textures table +* icon: Item icon +* drop: Dropped items after destroying minecart +* on_rightclick: Called after rightclick +* on_activate_by_rail: Called when above activator rail +* creative: If false, don't show in Creative Inventory +]] +local function register_minecart(def) + assert( def.drop, "def.drop is required parameter" ) + assert( def.itemstring, "def.itemstring is required parameter" ) + + local entity_id = def.entity_id; def.entity_id = nil + local craft = def.craft; def.craft = nil + local itemstring = def.itemstring; def.itemstring = nil + + -- Build cart definition + local cart = table.copy(DEFAULT_CART_DEF) + table_merge(cart, def) + minetest.register_entity(entity_id, cart) + + -- Register item to entity mapping + entity_mapping[itemstring] = entity_id + + register_minecart_craftitem(itemstring, def) + if minetest.get_modpath("doc_identifier") then + doc.sub.identifier.register_object(entity_id, "craftitems", itemstring) + end + + if craft then + minetest.register_craft(craft) + end +end + +-- Minecart +register_minecart({ + itemstring = "mcl_minecarts:minecart", + craft = { + output = "mcl_minecarts:minecart", + recipe = { + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot"}, + }, + }, + entity_id = "mcl_minecarts:minecart", + description = S("Minecart"), + tt_helop = S("Vehicle for fast travel on rails"), + long_descp = S("Minecarts can be used for a quick transportion on rails.") .. "\n" .. + S("Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type."), + S("You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.") .. "\n" .. + S("To obtain the minecart, punch it while holding down the sneak key.") .. "\n" .. + S("If it moves over a powered activator rail, you'll get ejected."), + initial_properties = { + mesh = "mcl_minecarts_minecart.b3d", + textures = {"mcl_minecarts_minecart.png"}, + }, + icon = "mcl_minecarts_minecart_normal.png", + drop = {"mcl_minecarts:minecart"}, + on_rightclick = function(self, clicker) + local name = clicker:get_player_name() + if not clicker or not clicker:is_player() then + return + end + local player_name = clicker:get_player_name() + if self._driver and player_name == self._driver then + --detach_driver(self) + elseif not self._driver and not clicker:get_player_control().sneak then + self._driver = player_name + self._start_pos = self.object:get_pos() + mcl_player.player_attached[player_name] = true + clicker:set_attach(self.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) + mcl_player.player_attached[name] = true + minetest.after(0.2, function(name) + local player = minetest.get_player_by_name(name) + if player then + mcl_player.player_set_animation(player, "sit" , 30) + player:set_eye_offset(vector.new(0,-5.5,0), vector.new(0,-4,0)) + mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) + end + end, name) + end + end, + on_activate_by_rail = activate_normal_minecart, + _mcl_minecarts_on_step = function(self, dtime) + -- Grab mob + if math.random(1,20) > 15 and not self._passenger then + local mobsnear = minetest.get_objects_inside_radius(self.object:get_pos(), 1.3) + for n=1, #mobsnear do + local mob = mobsnear[n] + if mob then + local entity = mob:get_luaentity() + if entity and entity.is_mob then + self._passenger = entity + mob:set_attach(self.object, "", PASSENGER_ATTACH_POSITION, vector.zero()) + break + end + end + end + elseif self._passenger then + local passenger_pos = self._passenger.object:get_pos() + if not passenger_pos then + self._passenger = nil + end + end + end +}) + +-- Minecart with Chest +register_minecart({ + itemstring = "mcl_minecarts:chest_minecart", + craft = { + output = "mcl_minecarts:chest_minecart", + recipe = { + {"mcl_chests:chest"}, + {"mcl_minecarts:minecart"}, + }, + }, + entity_id = "mcl_minecarts:chest_minecart", + description = S("Minecart with Chest"), + tt_help = nil, + longdesc = nil, + usagehelp = nil, + initial_properties = { + mesh = "mcl_minecarts_minecart_chest.b3d", + textures = { + "mcl_chests_normal.png", + "mcl_minecarts_minecart.png" + }, + }, + icon = "mcl_minecarts_minecart_chest.png", + drop = {"mcl_minecarts:minecart", "mcl_chests:chest"}, + groups = { container = 1 }, + on_rightclick = nil, + on_activate_by_rail = nil, + creative = true +}) +mcl_entity_invs.register_inv("mcl_minecarts:chest_minecart","Minecart",27,false,true) + +-- Minecart with Furnace +register_minecart({ + itemstring = "mcl_minecarts:furnace_minecart", + craft = { + output = "mcl_minecarts:furnace_minecart", + recipe = { + {"mcl_furnaces:furnace"}, + {"mcl_minecarts:minecart"}, + }, + }, + entity_id = "mcl_minecarts:furnace_minecart", + description = S("Minecart with Furnace"), + tt_help = nil, + longdesc = S("A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel."), + usagehelp = S("Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.") .. "\n" .. + S("To obtain the minecart and furnace, punch them while holding down the sneak key."), + + initial_properties = { + mesh = "mcl_minecarts_minecart_block.b3d", + textures = { + "default_furnace_top.png", + "default_furnace_top.png", + "default_furnace_front.png", + "default_furnace_side.png", + "default_furnace_side.png", + "default_furnace_side.png", + "mcl_minecarts_minecart.png", + }, + }, + icon = "mcl_minecarts_minecart_furnace.png", + drop = {"mcl_minecarts:minecart", "mcl_furnaces:furnace"}, + on_rightclick = function(self, clicker) + local staticdata = self._staticdata + + -- Feed furnace with coal + if not clicker or not clicker:is_player() then + return + end + local held = clicker:get_wielded_item() + if minetest.get_item_group(held:get_name(), "coal") == 1 then + staticdata.fueltime = (staticdata.fueltime or 0) + 180 + + -- Trucate to 27 minutes (9 uses) + if staticdata.fueltime > 27*60 then + staticdata.fuel_time = 27*60 + end + + if not minetest.is_creative_enabled(clicker:get_player_name()) then + held:take_item() + local index = clicker:get_wield_index() + local inv = clicker:get_inventory() + inv:set_stack("main", index, held) + end + self.object:set_properties({textures = + { + "default_furnace_top.png", + "default_furnace_top.png", + "default_furnace_front_active.png", + "default_furnace_side.png", + "default_furnace_side.png", + "default_furnace_side.png", + "mcl_minecarts_minecart.png", + }}) + end + end, + on_activate_by_rail = nil, + creative = true, + _mcl_minecarts_on_step = function(self, dtime) + local staticdata = self._staticdata + + -- Update furnace stuff + if (staticdata.fueltime or 0) > 0 then + staticdata.fueltime = (staticdata.fueltime or dtime) - dtime + if staticdata.fueltime <= 0 then + self.object:set_properties({textures = + { + "default_furnace_top.png", + "default_furnace_top.png", + "default_furnace_front.png", + "default_furnace_side.png", + "default_furnace_side.png", + "default_furnace_side.png", + "mcl_minecarts_minecart.png", + }}) + staticdata.fueltime = 0 + end + end + end +}) +function table_metadata(table) + return { + table = table, + set_string = function(self, key, value) + --print("set_string("..tostring(key)..", "..tostring(value)..")") + self.table[key] = tostring(value) + end, + get_string = function(self, key) + if self.table[key] then + return tostring(self.table[key]) + end + end + } +end + +-- Minecart with Command Block +register_minecart({ + itemstring = "mcl_minecarts:command_block_minecart", + entity_id = "mcl_minecarts:command_block_minecart", + description = S("Minecart with Command Block"), + tt_help = nil, + loncdesc = nil, + usagehelp = nil, + initial_properties = { + mesh = "mcl_minecarts_minecart_block.b3d", + textures = { + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "mcl_minecarts_minecart.png", + }, + }, + icon = "mcl_minecarts_minecart_command_block.png", + drop = {"mcl_minecarts:minecart"}, + on_rightclick = function(self, clicker) + self._staticdata.meta = self._staticdata.meta or {} + local meta = table_metadata(self._staticdata.meta) + + mesecon.commandblock.handle_rightclick(meta, clicker) + end, + _mcl_minecarts_on_place = function(self, placer) + -- Create a fake metadata object that stores into the cart's staticdata + self._staticdata.meta = self._staticdata.meta or {} + local meta = table_metadata(self._staticdata.meta) + + mesecon.commandblock.initialize(meta) + mesecon.commandblock.place(meta, placer) + end, + on_activate_by_rail = function(self, timer) + self._staticdata.meta = self._staticdata.meta or {} + local meta = table_metadata(self._staticdata.meta) + + mesecon.commandblock.action_on(meta, self.object:get_pos()) + end, + creative = true +}) + +-- Minecart with Hopper +register_minecart({ + itemstring = "mcl_minecarts:hopper_minecart", + craft = { + output = "mcl_minecarts:hopper_minecart", + recipe = { + {"mcl_hoppers:hopper"}, + {"mcl_minecarts:minecart"}, + }, + }, + entity_id = "mcl_minecarts:hopper_minecart", + description = S("Minecart with Hopper"), + tt_help = nil, + longdesc = nil, + usagehelp = nil, + initial_properties = { + mesh = "mcl_minecarts_minecart_hopper.b3d", + textures = { + "mcl_hoppers_hopper_inside.png", + "mcl_minecarts_minecart.png", + "mcl_hoppers_hopper_outside.png", + "mcl_hoppers_hopper_top.png", + }, + }, + icon = "mcl_minecarts_minecart_hopper.png", + drop = {"mcl_minecarts:minecart", "mcl_hoppers:hopper"}, + groups = { container = 1 }, + on_rightclick = nil, + on_activate_by_rail = nil, + _mcl_minecarts_on_enter = function(self, pos) + local staticdata = self._staticdata + if (staticdata.hopper_delay or 0) > 0 then + return + end + + -- try to pull from containers into our inventory + local inv = mcl_entity_invs.load_inv(self,5) + local above_pos = pos + vector.new(0,1,0) + mcl_util.hopper_pull_to_inventory(inv, 'main', above_pos, pos) + + staticdata.hopper_delay = (staticdata.hopper_delay or 0) + (1/20) + end, + _mcl_minecarts_on_step = function(self, dtime) + hopper_take_item(self, dtime) + end, + creative = true +}) +mcl_entity_invs.register_inv("mcl_minecarts:hopper_minecart", "Hopper Minecart", 5, false, true) + +-- Minecart with TNT +local function activate_tnt_minecart(self, timer) + if self._boomtimer then + return + end + if timer then + self._boomtimer = timer + else + self._boomtimer = tnt.BOOMTIMER + end + self.object:set_properties({textures = { + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_minecarts_minecart.png", + }}) + self._blinktimer = tnt.BLINKTIMER + minetest.sound_play("tnt_ignite", {pos = self.object:get_pos(), gain = 1.0, max_hear_distance = 15}, true) +end +register_minecart({ + itemstring = "mcl_minecarts:tnt_minecart", + craft = { + output = "mcl_minecarts:tnt_minecart", + recipe = { + {"mcl_tnt:tnt"}, + {"mcl_minecarts:minecart"}, + }, + }, + entity_id = "mcl_minecarts:tnt_minecart", + description = S("Minecart with TNT"), + tt_help = S("Vehicle for fast travel on rails").."\n"..S("Can be ignited by tools or powered activator rail"), + longdesc = S("A minecart with TNT is an explosive vehicle that travels on rail."), + usagehelp = S("Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.") .. "\n" .. + S("To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited."), + initial_properties = { + mesh = "mcl_minecarts_minecart_block.b3d", + textures = { + "default_tnt_top.png", + "default_tnt_bottom.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "mcl_minecarts_minecart.png", + }, + }, + icon = "mcl_minecarts_minecart_tnt.png", + drop = {"mcl_minecarts:minecart", "mcl_tnt:tnt"}, + on_rightclick = function(self, clicker) + -- Ingite + if not clicker or not clicker:is_player() then + return + end + if self._boomtimer then + return + end + local held = clicker:get_wielded_item() + if held:get_name() == "mcl_fire:flint_and_steel" then + if not minetest.is_creative_enabled(clicker:get_player_name()) then + held:add_wear(65535/65) -- 65 uses + local index = clicker:get_wield_index() + local inv = clicker:get_inventory() + inv:set_stack("main", index, held) + end + activate_tnt_minecart(self) + end + end, + on_activate_by_rail = activate_tnt_minecart, + creative = true, + _mcl_minecarts_on_step = function(self, dtime) + -- Update TNT stuff + if self._boomtimer then + -- Explode + self._boomtimer = self._boomtimer - dtime + local pos = self.object:get_pos() + if self._boomtimer <= 0 then + self.object:remove() + mcl_explosions.explode(pos, 4, { drop_chance = 1.0 }) + return + else + tnt.smoke_step(pos) + end + end + if self._blinktimer then + self._blinktimer = self._blinktimer - dtime + if self._blinktimer <= 0 then + self._blink = not self._blink + if self._blink then + self.object:set_properties({textures = + { + "default_tnt_top.png", + "default_tnt_bottom.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "mcl_minecarts_minecart.png", + }}) + else + self.object:set_properties({textures = + { + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_minecarts_minecart.png", + }}) + end + self._blinktimer = tnt.BLINKTIMER + end + end + end, +}) + +if minetest.get_modpath("mcl_wip") then + mcl_wip.register_wip_item("mcl_minecarts:chest_minecart") + mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart") + mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart") +end diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 426e534f1..d0fbb0b64 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -1,1415 +1,11 @@ local modname = minetest.get_current_modname() -local S = minetest.get_translator(modname) - -local has_mcl_wip = minetest.get_modpath("mcl_wip") mcl_minecarts = {} local mod = mcl_minecarts mcl_minecarts.modpath = minetest.get_modpath(modname) mcl_minecarts.speed_max = 10 mcl_minecarts.check_float_time = 15 -local max_step_distance = 0.5 -local friction = 0.4 -local MINECART_MAX_HP = 4 dofile(mcl_minecarts.modpath.."/functions.lua") dofile(mcl_minecarts.modpath.."/rails.lua") - -local LOGGING_ON = minetest.settings:get_bool("mcl_logging_minecarts", false) -local DEBUG = false -local function mcl_log(message) - if LOGGING_ON then - mcl_util.mcl_log(message, "[Minecarts]", true) - end -end - -mcl_minecarts.on_enter_below = function(pos, cart, next_dir, node_def) - local staticdata = cart._staticdata - if (node_def.groups.hopper or 0) == 0 then return end - - local hopper_pulled = mcl_hoppers.pull_from_minecart( cart, pos, cart._inv_size or 0 ) - if DEBUG then print( "Attempt pull_from_minecart, hopper_pulled="..tostring(hopper_pulled) ) end - - if hopper_pulled and next_dir ~= staticdata.dir then - -- If there was an item pulled by a hopper under the rails force the cart to stay put for 1.5 seconds - -- to allow redstone time to process - if hopper_pulled then - staticdata.delay = 1.5 - end - end -end - -local function detach_minecart(self) - local staticdata = self._staticdata - - staticdata.connected_at = nil - self.object:set_velocity(staticdata.dir * staticdata.velocity) -end -local function try_detach_minecart(self) - local staticdata = self._staticdata - - local node = minetest.get_node(staticdata.connected_at) - if minetest.get_item_group(node.name, "rail") == 0 then - detach_minecart(self) - end -end - ---[[ - Array of hooks { {u,v,w}, name } - Actual position is pos + u * dir + v * right + w * up -]] -local enter_exit_checks = { - { 0, 0, 0, "" }, - { 0, 0, 1, "_above" }, - { 0, 0,-1, "_below" }, - { 0, 1, 0, "_side" }, - { 0,-1, 0, "_side" }, -} - -local function handle_cart_enter_exit(self, pos, next_dir, event) - local staticdata = self._staticdata - - local dir = staticdata.dir - local right = vector.new( dir.z, dir.y, -dir.x) - local up = vector.new(0,1,0) - for _,check in ipairs(enter_exit_checks) do - local check_pos = pos + dir * check[1] + right * check[2] + up * check[3] - local node = minetest.get_node(check_pos) - local node_def = minetest.registered_nodes[node.name] - - -- node-specific hook - local hook_name = "_mcl_minecarts_"..event..check[4] - local hook = node_def[hook_name] - if hook then hook(check_pos, self, next_dir, pos) end - - -- global minecart hook - hook = mcl_minecarts[event..check[4]] - if hook then hook(check_pos, self, next_dir, node_def) end - end - - -- Handle cart-specific behaviors - local hook = self["_mcl_minecarts_"..event] - if hook then hook(self, pos) end -end -local function handle_cart_enter(self, pos, next_dir) - handle_cart_enter_exit(self, pos, next_dir, "on_enter" ) -end -local function handle_cart_leave(self, pos, next_dir) - handle_cart_enter_exit(self, pos, next_dir, "on_leave" ) -end - -local function handle_cart_node_watches(self, dtime) - local staticdata = self._staticdata - local watches = staticdata.node_watches or {} - local new_watches = {} - for _,node_pos in ipairs(watches) do - local node = minetest.get_node(node_pos) - local node_def = minetest.registered_nodes[node.name] - local hook = node_def._mcl_minecarts_node_on_step - if hook and hook(node_pos, self, dtime) then - new_watches[#new_watches+1] = node_pos - end - end - - staticdata.node_watches = new_watches -end - -local function update_cart_orientation(self,staticdata) - -- constants - local _2_pi = math.pi * 2 - local pi = math.pi - local dir = staticdata.dir - - -- Calculate an angle from the x,z direction components - local rot_y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) - if rot_y < 0 then - rot_y = rot_y + _2_pi - end - - -- Check if the rotation is a 180 flip and don't change if so - local rot = self.object:get_rotation() - local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) - if diff < 0.001 or diff > _2_pi - 0.001 then - -- Update rotation adjust and recalculate the rotation - staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi - rot.y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) - else - rot.y = rot_y - end - - -- Forward/backwards tilt (pitch) - if dir.y < 0 then - rot.x = -0.25 * pi - elseif dir.y > 0 then - rot.x = 0.25 * pi - else - rot.x = 0 - end - - if ( staticdata.rot_adjust or 0 ) < 0.01 then - rot.x = -rot.x - end - if dir.z ~= 0 then - rot.x = -rot.x - end - - self.object:set_rotation(rot) -end - -local function direction_away_from_players(self, staticdata) - local objs = minetest.get_objects_inside_radius(self.object:get_pos(), 1.1) - for n=1,#objs do - local obj = objs[n] - local player_name = obj:get_player_name() - if player_name and player_name ~= "" and not ( self._driver and self._driver == player_name ) then - local diff = obj:get_pos() - self.object:get_pos() - - local length = vector.distance(vector.new(0,0,0),diff) - local vec = diff / length - local force = vector.dot( vec, vector.normalize(staticdata.dir) ) - - -- Check if this would push past the end of the track and don't move it it would - -- This prevents an oscillation that would otherwise occur - local dir = staticdata.dir - if force > 0 then - dir = -dir - end - if mcl_minecarts:is_rail( staticdata.connected_at + dir ) then - if force > 0.5 then - return -length * 4 - elseif force < -0.5 then - return length * 4 - end - end - end - end - - return 0 -end - -local function calculate_acceleration(self, staticdata) - local acceleration = 0 - - -- Fix up movement data - staticdata.velocity = staticdata.velocity or 0 - - -- Apply friction if moving - if staticdata.velocity > 0 then - acceleration = -friction - end - - local pos = staticdata.connected_at - local node_name = minetest.get_node(pos).name - local node_def = minetest.registered_nodes[node_name] - local max_vel = mcl_minecarts.speed_max - - if self._go_forward then - acceleration = 4 - elseif self._brake then - acceleration = -1.5 - elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then - acceleration = 0.6 - elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then - -- Standard friction - else - if node_def._rail_acceleration then - acceleration = node_def._rail_acceleration * 4 - end - end - - -- Factor in gravity after everything else - local gravity_strength = 2.45 --friction * 5 - if staticdata.dir.y < 0 then - acceleration = gravity_strength - friction - elseif staticdata.dir.y > 0 then - acceleration = -gravity_strength + friction - end - - return acceleration -end - -local function reverse_direction(self, staticdata) - -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at - local next_dir = -staticdata.dir - staticdata.connected_at = staticdata.connected_at + staticdata.dir - staticdata.distance = 1 - staticdata.distance - - -- recalculate direction - local next_dir,_ = mcl_minecarts:get_rail_direction(staticdata.connected_at, next_dir, nil, nil, staticdata.railtype) - staticdata.dir = next_dir -end - -local function do_movement_step(self, dtime) - local staticdata = self._staticdata - if not staticdata.connected_at then return 0 end - - -- Calculate timestep remaiing in this block - local x_0 = staticdata.distance or 0 - local remaining_in_block = 1 - x_0 - local a = calculate_acceleration(self, staticdata) - local v_0 = staticdata.velocity - - -- Repel minecarts - local away = direction_away_from_players(self, staticdata) - if away > 0 then - v_0 = away - elseif away < 0 then - reverse_direction(self, staticdata) - v_0 = -away - end - - if DEBUG and ( v_0 > 0 or a ~= 0 ) then - print( " cart #"..tostring(staticdata.cart_id).. - ": a="..tostring(a).. - ",v_0="..tostring(v_0).. - ",x_0="..tostring(x_0).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir).. - ",connected_at="..tostring(staticdata.connected_at).. - ",distance="..tostring(staticdata.distance) - ) - end - - -- Not moving - if a == 0 and v_0 == 0 then return 0 end - - -- Movement equation with acceleration: x_1 = x_0 + v_0 * t + 0.5 * a * t*t - local timestep - local stops_in_block = false - local inside = v_0 * v_0 + 2 * a * remaining_in_block - if inside < 0 then - -- Would stop or reverse direction inside this block, calculate time to v_1 = 0 - timestep = -v_0 / a - stops_in_block = true - elseif a ~= 0 then - -- Setting x_1 = x_0 + remaining_in_block, and solving for t gives: - timestep = ( math.sqrt( v_0 * v_0 + 2 * a * remaining_in_block) - v_0 ) / a - else - timestep = remaining_in_block / v_0 - end - - -- Truncate timestep to remaining time delta - if timestep > dtime then - timestep = dtime - end - - -- Truncate timestep to prevent v_1 from being larger that speed_max - local v_max = mcl_minecarts.speed_max - if (v_0 ~= v_max) and ( v_0 + a * timestep > v_max) then - timestep = ( v_max - v_0 ) / a - end - - -- Prevent infinite loops - if timestep <= 0 then return 0 end - - -- Calculate v_1 taking v_max into account - local v_1 = v_0 + a * timestep - if v_1 > v_max then - v_1 = v_max - elseif v_1 < friction / 5 then - v_1 = 0 - end - - -- Calculate x_1 - local x_1 = x_0 + timestep * v_0 + 0.5 * a * timestep * timestep - - -- Update position and velocity of the minecart - staticdata.velocity = v_1 - staticdata.distance = x_1 - - if DEBUG and (1==0) and ( v_0 > 0 or a ~= 0 ) then - print( "- cart #"..tostring(staticdata.cart_id).. - ": a="..tostring(a).. - ",v_0="..tostring(v_0).. - ",v_1="..tostring(v_1).. - ",x_0="..tostring(x_0).. - ",x_1="..tostring(x_1).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir).. - ",connected_at="..tostring(staticdata.connected_at).. - ",distance="..tostring(staticdata.distance) - ) - end - - - -- Entity movement - local pos = staticdata.connected_at - - -- Handle movement to next block, account for loss of precision in calculations - if x_1 >= 0.99 then - staticdata.distance = 0 - - -- Anchor at the next node - local old_pos = pos - pos = pos + staticdata.dir - staticdata.connected_at = pos - - -- Get the next direction - local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) - if DEBUG and next_dir ~= staticdata.dir then - print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) - end - - -- Leave the old node - handle_cart_leave(self, old_pos, next_dir ) - - -- Enter the new node - handle_cart_enter(self, pos, next_dir) - - try_detach_minecart(self) - - -- Handle end of track - if next_dir == staticdata.dir * -1 and next_dir.y == 0 then - if DEBUG then print("Stopping cart at end of track at "..tostring(pos)) end - staticdata.velocity = 0 - end - - -- Update cart direction - staticdata.dir = next_dir - elseif stops_in_block and v_1 < (friction/5) and a <= 0 then - -- Handle direction flip due to gravity - if DEBUG then print("Gravity flipped direction") end - - -- Velocity should be zero at this point - staticdata.velocity = 0 - - reverse_direction(self, staticdata) - - -- Intermediate movement - pos = staticdata.connected_at + staticdata.dir * staticdata.distance - else - -- Intermediate movement - pos = pos + staticdata.dir * staticdata.distance - end - - self.object:move_to(pos) - - -- Update cart orientation - update_cart_orientation(self,staticdata) - - -- Debug reporting - if DEBUG and ( v_0 > 0 or v_1 > 0 ) then - print( " cart #"..tostring(staticdata.cart_id).. - ": a="..tostring(a).. - ",v_0="..tostring(v_0).. - ",v_1="..tostring(v_1).. - ",x_0="..tostring(x_0).. - ",x_1="..tostring(x_1).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir).. - ",pos="..tostring(pos).. - ",connected_at="..tostring(staticdata.connected_at).. - ",distance="..tostring(staticdata.distance) - ) - end - - -- Report the amount of time processed - return dtime - timestep -end - - -local function do_movement( self, dtime ) - local staticdata = self._staticdata - - -- Allow the carts to be delay for the rest of the world to react before moving again - if ( staticdata.delay or 0 ) > dtime then - staticdata.delay = staticdata.delay - dtime - return - else - staticdata.delay = 0 - end - - -- Break long movements at block boundaries to make it - -- it impossible to jump across gaps due to server lag - -- causing large timesteps - while dtime > 0 do - local new_dtime = do_movement_step(self, dtime) - - -- Handle node watches here in steps to prevent server lag from changing behavior - handle_cart_node_watches(self, dtime - new_dtime) - - dtime = new_dtime - end -end - -local function do_detached_movement(self, dtime) - local staticdata = self._staticdata - - -- Apply physics - if mcl_physics then - mcl_physics.apply_entity_environmental_physics(self) - else - -- Simple physics - local friction = self.object:get_velocity() - friction.y = 0 - - local accel = vector.new(0,-9.81,0) -- gravity - accel = vector.add(accel, vector.multiply(friction,-0.9)) - self.object:set_acceleration(accel) - end - - -- Try to reconnect to rail - local pos_r = vector.round(self.object:get_pos()) - local node = minetest.get_node(pos_r) - if minetest.get_item_group(node.name, "rail") ~= 0 then - staticdata.connected_at = pos_r - staticdata.railtype = node.name - - local freebody_velocity = self.object:get_velocity() - staticdata.dir = mod:get_rail_direction(pos_r, mod.snap_direction(freebody_velocity)) - - -- Use vector projection to only keep the velocity in the new direction of movement on the rail - -- https://en.wikipedia.org/wiki/Vector_projection - staticdata.velocity = vector.dot(staticdata.dir,freebody_velocity) - - -- Clear freebody movement - self.object:set_velocity(vector.new(0,0,0)) - self.object:set_acceleration(vector.new(0,0,0)) - end -end - -local function detach_driver(self) - if not self._driver then - return - end - mcl_player.player_attached[self._driver] = nil - local player = minetest.get_player_by_name(self._driver) - self._driver = nil - self._start_pos = nil - if player then - player:set_detach() - player:set_eye_offset(vector.new(0,0,0),vector.new(0,0,0)) - mcl_player.player_set_animation(player, "stand" , 30) - end -end - -local function activate_tnt_minecart(self, timer) - if self._boomtimer then - return - end - if timer then - self._boomtimer = timer - else - self._boomtimer = tnt.BOOMTIMER - end - self.object:set_properties({textures = { - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_minecarts_minecart.png", - }}) - self._blinktimer = tnt.BLINKTIMER - minetest.sound_play("tnt_ignite", {pos = self.object:get_pos(), gain = 1.0, max_hear_distance = 15}, true) -end - -local function activate_normal_minecart(self) - detach_driver(self) - - -- Detach passenger - if self._passenger then - local mob = self._passenger.object - mob:set_detach() - end -end - -local function hopper_take_item(self, dtime) - local pos = self.object:get_pos() - if not pos then return end - - if not self or self.name ~= "mcl_minecarts:hopper_minecart" then return end - - if mcl_util.check_dtime_timer(self, dtime, "hoppermc_take", 0.15) then - --minetest.log("The check timer was triggered: " .. dump(pos) .. ", name:" .. self.name) - else - --minetest.log("The check timer was not triggered") - return - end - - --mcl_log("self.itemstring: ".. self.itemstring) - - local above_pos = vector.offset(pos, 0, 0.9, 0) - --mcl_log("self.itemstring: ".. minetest.pos_to_string(above_pos)) - local objs = minetest.get_objects_inside_radius(above_pos, 1.25) - - if objs then - mcl_log("there is an itemstring. Number of objs: ".. #objs) - - for k, v in pairs(objs) do - local ent = v:get_luaentity() - - if ent and not ent._removed and ent.itemstring and ent.itemstring ~= "" then - local taken_items = false - - mcl_log("ent.name: " .. tostring(ent.name)) - mcl_log("ent pos: " .. tostring(ent.object:get_pos())) - - local inv = mcl_entity_invs.load_inv(self, 5) - if not inv then return false end - - local current_itemstack = ItemStack(ent.itemstring) - - mcl_log("inv. size: " .. self._inv_size) - if inv:room_for_item("main", current_itemstack) then - mcl_log("Room") - inv:add_item("main", current_itemstack) - ent.object:get_luaentity().itemstring = "" - ent.object:remove() - taken_items = true - else - mcl_log("no Room") - end - - if not taken_items then - local items_remaining = current_itemstack:get_count() - - -- This will take part of a floating item stack if no slot can hold the full amount - for i = 1, self._inv_size, 1 do - local stack = inv:get_stack("main", i) - - mcl_log("i: " .. tostring(i)) - mcl_log("Items remaining: " .. items_remaining) - mcl_log("Name: " .. tostring(stack:get_name())) - - if current_itemstack:get_name() == stack:get_name() then - mcl_log("We have a match. Name: " .. tostring(stack:get_name())) - - local room_for = stack:get_stack_max() - stack:get_count() - mcl_log("Room for: " .. tostring(room_for)) - - if room_for == 0 then - -- Do nothing - mcl_log("No room") - elseif room_for < items_remaining then - mcl_log("We have more items remaining than space") - - items_remaining = items_remaining - room_for - stack:set_count(stack:get_stack_max()) - inv:set_stack("main", i, stack) - taken_items = true - else - local new_stack_size = stack:get_count() + items_remaining - stack:set_count(new_stack_size) - mcl_log("We have more than enough space. Now holds: " .. new_stack_size) - - inv:set_stack("main", i, stack) - items_remaining = 0 - - ent.object:get_luaentity().itemstring = "" - ent.object:remove() - - taken_items = true - break - end - - mcl_log("Count: " .. tostring(stack:get_count())) - mcl_log("stack max: " .. tostring(stack:get_stack_max())) - --mcl_log("Is it empty: " .. stack:to_string()) - end - - if i == self._inv_size and taken_items then - mcl_log("We are on last item and still have items left. Set final stack size: " .. items_remaining) - current_itemstack:set_count(items_remaining) - --mcl_log("Itemstack2: " .. current_itemstack:to_string()) - ent.itemstring = current_itemstack:to_string() - end - end - end - - --Add in, and delete - if taken_items then - mcl_log("Saving") - mcl_entity_invs.save_inv(ent) - return taken_items - else - mcl_log("No need to save") - end - - end - end - end - - return false -end - --- Table for item-to-entity mapping. Keys: itemstring, Values: Corresponding entity ID -local entity_mapping = {} - -local function make_staticdata( railtype, connected_at, dir ) - return { - railtype = railtype, - connected_at = connected_at, - distance = 0, - velocity = 0, - dir = vector.new(dir), - cart_id = math.random(1,1000000000), - } -end - -local function to_dirstring(dir) - if dir.x == 0 then - if dir.z == 1 then - return "north" - else - return "south" - end - elseif dir.z == 0 then - if dir.x == 1 then - return " east" - else - return " west" - end - end -end - -local function register_entity(entity_id, def) - assert( def.drop, "drop is required parameter" ) - - -- Entity groups - local groups = { minecart = 1 } - for k,v in pairs(def.groups or {}) do - groups[k] = v - end - - local cart = { - initial_properties = { - physical = true, - collisionbox = {-10/16., -0.5, -10/16, 10/16, 0.25, 10/16}, - visual = "mesh", - mesh = def.mesh, - visual_size = {x=1, y=1}, - textures = def.textures, - }, - - hp_max = MINECART_MAX_HP, - - groups = groups, - - on_rightclick = def.on_rightclick, - on_activate_by_rail = def.on_activate_by_rail, - - _mcl_minecarts_on_enter = def._mcl_minecarts_on_enter, - _mcl_minecarts_on_place = def._mcl_minecarts_on_place, - _mcl_minecarts_on_step = def._mcl_minecarts_on_step, - - _driver = nil, -- player who sits in and controls the minecart (only for minecart!) - _passenger = nil, -- for mobs - _start_pos = nil, -- Used to calculate distance for “On A Rail” achievement - _last_float_check = nil, -- timestamp of last time the cart was checked to be still on a rail - _boomtimer = nil, -- how many seconds are left before exploding - _blinktimer = nil, -- how many seconds are left before TNT blinking - _blink = false, -- is TNT blink texture active? - _old_pos = nil, - _staticdata = nil, - } - - function cart:on_activate(staticdata, dtime_s) - -- Initialize - local data = minetest.deserialize(staticdata) - if type(data) == "table" then - -- Migrate old data - if data._railtype then - data.railtype = data._railtype - data._railtype = nil - end - -- Fix up types - data.dir = vector.new(data.dir) - - -- Make sure all carts have an ID to isolate them - data.cart_id = staticdata.cart_id or math.random(1,1000000000) - - self._staticdata = data - end - - -- Activate cart if on activator rail - if self.on_activate_by_rail then - local pos = self.object:get_pos() - local node = minetest.get_node(vector.floor(pos)) - if node.name == "mcl_minecarts:activator_rail_on" then - self:on_activate_by_rail() - end - end - end - function cart:on_death(killer) - local staticdata = self._staticdata - detach_driver(self) - - -- Detach passenger - if self._passenger then - local mob = self._passenger.object - mob:set_detach() - end - - -- Leave nodes - if staticdata.attached_at then - handle_cart_leave(self, staticdata.attached_at, staticdata.dir ) - else - mcl_log("TODO: handle detatched minecart death") - end - - -- Drop items - local drop = def.drop - if not killer or not minetest.is_creative_enabled(killer:get_player_name()) then - for d=1, #drop do - minetest.add_item(self.object:get_pos(), drop[d]) - end - elseif killer and killer:is_player() then - local inv = killer:get_inventory() - for d=1, #drop do - if not inv:contains_item("main", drop[d]) then - inv:add_item("main", drop[d]) - end - end - end - end - - local passenger_attach_position = vector.new(0, -1.75, 0) - - function cart:add_node_watch(pos) - local staticdata = self._staticdata - local watches = staticdata.node_watches or {} - - for _,watch in ipairs(watches) do - if watch == pos then return end - end - - watches[#watches+1] = pos - staticdata.node_watches = watches - end - function cart:remove_node_watch(pos) - local staticdata = self._staticdata - local watches = staticdata.node_watches or {} - - local new_watches = {} - for _,node_pos in ipairs(watches) do - if node_pos ~= pos then - new_watches[#new_watches] = node_pos - end - end - staticdata.node_watches = new_watches - end - - function cart:on_step(dtime) - local staticdata = self._staticdata - if not staticdata then - staticdata = make_staticdata() - self._staticdata = staticdata - end - - -- Regen - local hp = self.object:get_hp() - if hp < MINECART_MAX_HP then - if (staticdata.regen_timer or 0) > 0.5 then - hp = hp + 1 - staticdata.regen_timer = staticdata.regen_timer - 1 - end - staticdata.regen_timer = (staticdata.regen_timer or 0) + dtime - self.object:set_hp(hp) - else - staticdata.regen_timer = nil - end - - -- Fix railtype field - local pos = self.object:get_pos() - if staticdata.connected_at and not staticdata.railtype then - local node = minetest.get_node(vector.floor(pos)).name - staticdata.railtype = minetest.get_item_group(node, "connect_to_raillike") - end - - -- Cart specific behaviors - local hook = self._mcl_minecarts_on_step - if hook then hook(self,dtime) end - - if (staticdata.hopper_delay or 0) > 0 then - staticdata.hopper_delay = staticdata.hopper_delay - dtime - end - - -- Controls - local ctrl, player = nil, nil - if self._driver then - player = minetest.get_player_by_name(self._driver) - if player then - ctrl = player:get_player_control() - -- player detach - if ctrl.sneak then - detach_driver(self) - return - end - - -- Experimental controls - self._go_forward = ctrl.up - self._brake = ctrl.down - end - - -- Give achievement when player reached a distance of 1000 nodes from the start position - if vector.distance(self._start_pos, pos) >= 1000 then - awards.unlock(self._driver, "mcl:onARail") - end - end - - if staticdata.connected_at then - do_movement(self, dtime) - else - do_detached_movement(self, dtime) - end - - -- TODO: move this into mcl_core:cactus _mcl_minecarts_on_enter_side - -- Drop minecart if it collides with a cactus node - local r = 0.6 - for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do - if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then - detach_driver(self) - local drop = def.drop - for d = 1, #drop do - minetest.add_item(pos, drop[d]) - end - self.object:remove() - return - end - end - end - - function cart:get_staticdata() - return minetest.serialize(self._staticdata or {}) - end - - minetest.register_entity(entity_id, cart) -end - --- Place a minecart at pointed_thing -function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) - if not pointed_thing.type == "node" then - return - end - - local railpos, node - if mcl_minecarts:is_rail(pointed_thing.under) then - railpos = pointed_thing.under - node = minetest.get_node(pointed_thing.under) - elseif mcl_minecarts:is_rail(pointed_thing.above) then - railpos = pointed_thing.above - node = minetest.get_node(pointed_thing.above) - else - return - end - - local entity_id = entity_mapping[itemstack:get_name()] - local cart = minetest.add_entity(railpos, entity_id) - local railtype = minetest.get_item_group(node.name, "connect_to_raillike") - local cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0), nil, nil, railtype) - cart:set_yaw(minetest.dir_to_yaw(cart_dir)) - - -- Update static data - local le = cart:get_luaentity() - if le then - le._staticdata = make_staticdata( railtype, railpos, cart_dir ) - end - - -- Call placer - if le._mcl_minecarts_on_place then - le._mcl_minecarts_on_place(le, placer) - end - - handle_cart_enter(le, railpos) - - local pname = "" - if placer then - pname = placer:get_player_name() - end - if not minetest.is_creative_enabled(pname) then - itemstack:take_item() - end - return itemstack -end - -local function dropper_place_minecart(dropitem, pos) - local node = minetest.get_node(pos) - local nodedef = minetest.registered_nodes[node.name] - - -- Don't try to place the minecart if pos isn't a rail - if (nodedef.groups.rail or 0) == 0 then return false end - - mcl_minecarts.place_minecart(dropitem, { - above = pos, - under = vector.offset(pos,0,-1,0) - }) - return true -end - -local function register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative) - entity_mapping[itemstring] = entity_id - - local groups = { minecart = 1, transport = 1 } - if creative == false then - groups.not_in_creative_inventory = 1 - end - local def = { - stack_max = 1, - _mcl_dropper_on_drop = dropper_place_minecart, - on_place = function(itemstack, placer, pointed_thing) - if not pointed_thing.type == "node" then - return - end - - -- Call on_rightclick if the pointed node defines it - local node = minetest.get_node(pointed_thing.under) - if placer and not placer:get_player_control().sneak then - if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then - return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack - end - end - - return mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) - end, - _on_dispense = function(stack, pos, droppos, dropnode, dropdir) - -- Place minecart as entity on rail. If there's no rail, just drop it. - local placed - if minetest.get_item_group(dropnode.name, "rail") ~= 0 then - -- FIXME: This places minecarts even if the spot is already occupied - local pointed_thing = { under = droppos, above = vector.new( droppos.x, droppos.y+1, droppos.z ) } - placed = mcl_minecarts.place_minecart(stack, pointed_thing) - end - if placed == nil then - -- Drop item - minetest.add_item(droppos, stack) - end - end, - groups = groups, - } - def.description = description - def._tt_help = tt_help - def._doc_items_longdesc = longdesc - def._doc_items_usagehelp = usagehelp - def.inventory_image = icon - def.wield_image = icon - minetest.register_craftitem(itemstring, def) -end - ---[[ -Register a minecart -* itemstring: Itemstring of minecart item -* entity_id: ID of minecart entity -* description: Item name / description -* longdesc: Long help text -* usagehelp: Usage help text -* mesh: Minecart mesh -* textures: Minecart textures table -* icon: Item icon -* drop: Dropped items after destroying minecart -* on_rightclick: Called after rightclick -* on_activate_by_rail: Called when above activator rail -* creative: If false, don't show in Creative Inventory -]] -local function register_minecart(def) - register_entity(def.entity_id, def) - register_craftitem(def.itemstring, def.entity_id, def.description, def.tt_help, def.longdesc, def.usagehelp, def.icon, def.creative) - if minetest.get_modpath("doc_identifier") then - doc.sub.identifier.register_object(def.entity_id, "craftitems", def.itemstring) - end -end - --- Minecart -register_minecart({ - itemstring = "mcl_minecarts:minecart", - entity_id = "mcl_minecarts:minecart", - description = S("Minecart"), - tt_helop = S("Vehicle for fast travel on rails"), - long_descp = S("Minecarts can be used for a quick transportion on rails.") .. "\n" .. - S("Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type."), - S("You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.") .. "\n" .. - S("To obtain the minecart, punch it while holding down the sneak key.") .. "\n" .. - S("If it moves over a powered activator rail, you'll get ejected."), - mesh = "mcl_minecarts_minecart.b3d", - textures = {"mcl_minecarts_minecart.png"}, - icon = "mcl_minecarts_minecart_normal.png", - drop = {"mcl_minecarts:minecart"}, - on_rightclick = function(self, clicker) - local name = clicker:get_player_name() - if not clicker or not clicker:is_player() then - return - end - local player_name = clicker:get_player_name() - if self._driver and player_name == self._driver then - --detach_driver(self) - elseif not self._driver and not clicker:get_player_control().sneak then - self._driver = player_name - self._start_pos = self.object:get_pos() - mcl_player.player_attached[player_name] = true - clicker:set_attach(self.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) - mcl_player.player_attached[name] = true - minetest.after(0.2, function(name) - local player = minetest.get_player_by_name(name) - if player then - mcl_player.player_set_animation(player, "sit" , 30) - player:set_eye_offset(vector.new(0,-5.5,0), vector.new(0,-4,0)) - mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) - end - end, name) - end - end, - on_activate_by_rail = activate_normal_minecart, - _mcl_minecarts_on_step = function(self, dtime) - -- Grab mob - if math.random(1,20) > 15 and not self._passenger then - if self.name == "mcl_minecarts:minecart" then - local mobsnear = minetest.get_objects_inside_radius(self.object:get_pos(), 1.3) - for n=1, #mobsnear do - local mob = mobsnear[n] - if mob then - local entity = mob:get_luaentity() - if entity and entity.is_mob then - self._passenger = entity - mob:set_attach(self.object, "", passenger_attach_position, vector.zero()) - break - end - end - end - end - elseif self._passenger then - local passenger_pos = self._passenger.object:get_pos() - if not passenger_pos then - self._passenger = nil - end - end - end -}) - --- Minecart with Chest -register_minecart({ - itemstring = "mcl_minecarts:chest_minecart", - entity_id = "mcl_minecarts:chest_minecart", - description = S("Minecart with Chest"), - tt_help = nil, - longdesc = nil, - usagehelp = nil, - mesh = "mcl_minecarts_minecart_chest.b3d", - textures = { - "mcl_chests_normal.png", - "mcl_minecarts_minecart.png" - }, - icon = "mcl_minecarts_minecart_chest.png", - drop = {"mcl_minecarts:minecart", "mcl_chests:chest"}, - groups = { container = 1 }, - on_rightclick = nil, - on_activate_by_rail = nil, - creative = true -}) -mcl_entity_invs.register_inv("mcl_minecarts:chest_minecart","Minecart",27,false,true) - --- Minecart with Furnace -register_minecart({ - itemstring = "mcl_minecarts:furnace_minecart", - entity_id = "mcl_minecarts:furnace_minecart", - description = S("Minecart with Furnace"), - tt_help = nil, - longdesc = S("A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel."), - usagehelp = S("Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.") .. "\n" .. - S("To obtain the minecart and furnace, punch them while holding down the sneak key."), - - mesh = "mcl_minecarts_minecart_block.b3d", - textures = { - "default_furnace_top.png", - "default_furnace_top.png", - "default_furnace_front.png", - "default_furnace_side.png", - "default_furnace_side.png", - "default_furnace_side.png", - "mcl_minecarts_minecart.png", - }, - icon = "mcl_minecarts_minecart_furnace.png", - drop = {"mcl_minecarts:minecart", "mcl_furnaces:furnace"}, - on_rightclick = function(self, clicker) - local staticdata = self._staticdata - - -- Feed furnace with coal - if not clicker or not clicker:is_player() then - return - end - local held = clicker:get_wielded_item() - if minetest.get_item_group(held:get_name(), "coal") == 1 then - staticdata.fueltime = (staticdata.fueltime or 0) + 180 - - -- Trucate to 27 minutes (9 uses) - if staticdata.fueltime > 27*60 then - staticdata.fuel_time = 27*60 - end - - if not minetest.is_creative_enabled(clicker:get_player_name()) then - held:take_item() - local index = clicker:get_wield_index() - local inv = clicker:get_inventory() - inv:set_stack("main", index, held) - end - self.object:set_properties({textures = - { - "default_furnace_top.png", - "default_furnace_top.png", - "default_furnace_front_active.png", - "default_furnace_side.png", - "default_furnace_side.png", - "default_furnace_side.png", - "mcl_minecarts_minecart.png", - }}) - end - end, - on_activate_by_rail = nil, - creative = true, - _mcl_minecarts_on_step = function(self, dtime) - local staticdata = self._staticdata - - -- Update furnace stuff - if (staticdata.fueltime or 0) > 0 then - staticdata.fueltime = (staticdata.fueltime or dtime) - dtime - if staticdata.fueltime <= 0 then - self.object:set_properties({textures = - { - "default_furnace_top.png", - "default_furnace_top.png", - "default_furnace_front.png", - "default_furnace_side.png", - "default_furnace_side.png", - "default_furnace_side.png", - "mcl_minecarts_minecart.png", - }}) - staticdata.fueltime = 0 - end - end - end -}) -function table_metadata(table) - return { - table = table, - set_string = function(self, key, value) - --print("set_string("..tostring(key)..", "..tostring(value)..")") - self.table[key] = tostring(value) - end, - get_string = function(self, key) - if self.table[key] then - return tostring(self.table[key]) - end - end - } -end - --- Minecart with Command Block -register_minecart({ - itemstring = "mcl_minecarts:command_block_minecart", - entity_id = "mcl_minecarts:command_block_minecart", - description = S("Minecart with Command Block"), - tt_help = nil, - loncdesc = nil, - usagehelp = nil, - mesh = "mcl_minecarts_minecart_block.b3d", - textures = { - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "mcl_minecarts_minecart.png", - }, - icon = "mcl_minecarts_minecart_command_block.png", - drop = {"mcl_minecarts:minecart"}, - on_rightclick = function(self, clicker) - self._staticdata.meta = self._staticdata.meta or {} - local meta = table_metadata(self._staticdata.meta) - - mesecon.commandblock.handle_rightclick(meta, clicker) - end, - _mcl_minecarts_on_place = function(self, placer) - -- Create a fake metadata object that stores into the cart's staticdata - self._staticdata.meta = self._staticdata.meta or {} - local meta = table_metadata(self._staticdata.meta) - - mesecon.commandblock.initialize(meta) - mesecon.commandblock.place(meta, placer) - end, - on_activate_by_rail = function(self, timer) - self._staticdata.meta = self._staticdata.meta or {} - local meta = table_metadata(self._staticdata.meta) - - mesecon.commandblock.action_on(meta, self.object:get_pos()) - end, - creative = true -}) - --- Minecart with Hopper -register_minecart({ - itemstring = "mcl_minecarts:hopper_minecart", - entity_id = "mcl_minecarts:hopper_minecart", - description = S("Minecart with Hopper"), - tt_help = nil, - longdesc = nil, - usagehelp = nil, - mesh = "mcl_minecarts_minecart_hopper.b3d", - textures = { - "mcl_hoppers_hopper_inside.png", - "mcl_minecarts_minecart.png", - "mcl_hoppers_hopper_outside.png", - "mcl_hoppers_hopper_top.png", - }, - icon = "mcl_minecarts_minecart_hopper.png", - drop = {"mcl_minecarts:minecart", "mcl_hoppers:hopper"}, - groups = { container = 1 }, - on_rightclick = nil, - on_activate_by_rail = nil, - _mcl_minecarts_on_enter = function(self, pos) - local staticdata = self._staticdata - if (staticdata.hopper_delay or 0) > 0 then - return - end - - -- try to pull from containers into our inventory - local inv = mcl_entity_invs.load_inv(self,5) - local above_pos = pos + vector.new(0,1,0) - mcl_util.hopper_pull_to_inventory(inv, 'main', above_pos, pos) - - staticdata.hopper_delay = (staticdata.hopper_delay or 0) + (1/20) - end, - _mcl_minecarts_on_step = function(self, dtime) - hopper_take_item(self, dtime) - end, - creative = true -}) -mcl_entity_invs.register_inv("mcl_minecarts:hopper_minecart", "Hopper Minecart", 5, false, true) - --- Minecart with TNT -register_minecart({ - itemstring = "mcl_minecarts:tnt_minecart", - entity_id = "mcl_minecarts:tnt_minecart", - description = S("Minecart with TNT"), - tt_help = S("Vehicle for fast travel on rails").."\n"..S("Can be ignited by tools or powered activator rail"), - longdesc = S("A minecart with TNT is an explosive vehicle that travels on rail."), - usagehelp = S("Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.") .. "\n" .. - S("To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited."), - mesh = "mcl_minecarts_minecart_block.b3d", - textures = { - "default_tnt_top.png", - "default_tnt_bottom.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "mcl_minecarts_minecart.png", - }, - icon = "mcl_minecarts_minecart_tnt.png", - drop = {"mcl_minecarts:minecart", "mcl_tnt:tnt"}, - on_rightclick = function(self, clicker) - -- Ingite - if not clicker or not clicker:is_player() then - return - end - if self._boomtimer then - return - end - local held = clicker:get_wielded_item() - if held:get_name() == "mcl_fire:flint_and_steel" then - if not minetest.is_creative_enabled(clicker:get_player_name()) then - held:add_wear(65535/65) -- 65 uses - local index = clicker:get_wield_index() - local inv = clicker:get_inventory() - inv:set_stack("main", index, held) - end - activate_tnt_minecart(self) - end - end, - on_activate_by_rail = activate_tnt_minecart, - creative = true, - _mcl_minecarts_on_step = function(self, dtime) - -- Update TNT stuff - if self._boomtimer then - -- Explode - self._boomtimer = self._boomtimer - dtime - local pos = self.object:get_pos() - if self._boomtimer <= 0 then - self.object:remove() - mcl_explosions.explode(pos, 4, { drop_chance = 1.0 }) - return - else - tnt.smoke_step(pos) - end - end - if self._blinktimer then - self._blinktimer = self._blinktimer - dtime - if self._blinktimer <= 0 then - self._blink = not self._blink - if self._blink then - self.object:set_properties({textures = - { - "default_tnt_top.png", - "default_tnt_bottom.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "mcl_minecarts_minecart.png", - }}) - else - self.object:set_properties({textures = - { - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_minecarts_minecart.png", - }}) - end - self._blinktimer = tnt.BLINKTIMER - end - end - end, -}) - - -minetest.register_craft({ - output = "mcl_minecarts:minecart", - recipe = { - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot"}, - }, -}) - -minetest.register_craft({ - output = "mcl_minecarts:tnt_minecart", - recipe = { - {"mcl_tnt:tnt"}, - {"mcl_minecarts:minecart"}, - }, -}) - -minetest.register_craft({ - output = "mcl_minecarts:furnace_minecart", - recipe = { - {"mcl_furnaces:furnace"}, - {"mcl_minecarts:minecart"}, - }, -}) - -minetest.register_craft({ - output = "mcl_minecarts:hopper_minecart", - recipe = { - {"mcl_hoppers:hopper"}, - {"mcl_minecarts:minecart"}, - }, -}) - - -minetest.register_craft({ - output = "mcl_minecarts:chest_minecart", - recipe = { - {"mcl_chests:chest"}, - {"mcl_minecarts:minecart"}, - }, -}) - - -if has_mcl_wip then - mcl_wip.register_wip_item("mcl_minecarts:chest_minecart") - mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart") - mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart") -end +dofile(mcl_minecarts.modpath.."/carts.lua") diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 8b373369f..f5ebaa40e 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -562,6 +562,9 @@ end local CURVY_RAILS_MAP = { ["mcl_minecarts:rail"] = "mcl_minecarts:rail_v2", } +for old,new in pairs(CURVY_RAILS_MAP) do + minetest.register_alias(old, new) +end minetest.register_lbm({ name = "mcl_minecarts:update_legacy_curvy_rails", nodenames = mcl_util.table_keys(CURVY_RAILS_MAP), @@ -581,6 +584,9 @@ local STRAIGHT_RAILS_MAP ={ ["mcl_minecarts:detector_rail"] = "mcl_minecarts:detector_rail_v2", ["mcl_minecarts:detector_rail_on"] = "mcl_minecarts:detector_rail_v2_on", } +for old,new in pairs(STRAIGHT_RAILS_MAP) do + minetest.register_alias(old, new) +end minetest.register_lbm({ name = "mcl_minecarts:update_legacy_straight_rails", nodenames = mcl_util.table_keys(STRAIGHT_RAILS_MAP), From 60ee3a06b9c2e41ed11950ec2fd75dbe7ccfdd66 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 6 Apr 2024 05:56:21 +0000 Subject: [PATCH 058/195] Fix rail movement regressions --- mods/ENTITIES/mcl_minecarts/functions.lua | 16 ++++++++-------- mods/ENTITIES/mcl_minecarts/rails.lua | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 2ed625e02..a991793be 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -296,19 +296,19 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) dir = node_def._mcl_minecarts.get_next_dir(pos, dir, node) - -- Handle going downhill - if is_ahead_slope(pos,dir) then - dir = vector.offset(dir,0,-1,0) - end - -- Handle reversing if there is a solid block in the next position local next_pos = vector.add(pos, dir) local next_node = minetest.get_node(next_pos) local node_def = minetest.registered_nodes[next_node.name] if node_def and node_def.groups and ( node_def.groups.solid or node_def.groups.stair ) then -- Reverse the direction without giving -0 members - return vector.direction(next_pos, pos) - else - return dir + dir = vector.direction(next_pos, pos) end + + -- Handle going downhill + if is_ahead_slope(pos,dir) then + dir = vector.offset(dir,0,-1,0) + end + + return dir end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index f5ebaa40e..45498c89c 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -33,6 +33,8 @@ end --- Rail direction Handleres local function rail_dir_straight(pos, dir, node) + dir = vector.new(dir.x, 0, dir.z) + if node.param2 == 0 or node.param2 == 2 then if vector.equals(dir, north) then return north @@ -59,6 +61,8 @@ local function rail_dir_sloped(pos, dir, node) end end local function rail_dir_curve(pos, dir, node) + dir = vector.new(dir.x, 0, dir.z) + if node.param2 == 0 then -- north -- South and East if vector.equals(dir, south) then return south end @@ -86,10 +90,14 @@ local function rail_dir_curve(pos, dir, node) end end local function rail_dir_tee(pos, dir, node) + dir = vector.new(dir.x, 0, dir.z) + minetest.log("warning","TODO: implement rail_dir_tee()") return north end local function rail_dir_cross(pos, dir, node) + dir = vector.new(dir.x, 0, dir.z) + -- Always continue in the same direction. No direction changes allowed return dir end From f089d0a702a827b9781f7c39f4e7f0d980b7bcab Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 6 Apr 2024 06:24:31 +0000 Subject: [PATCH 059/195] Fix rails in creative inventory, make minecart with tnt not crash server when exploding, make minecart with tnt slightly more powerful than regular tnt --- mods/ENTITIES/mcl_minecarts/carts.lua | 7 +++++-- mods/ENTITIES/mcl_minecarts/rails.lua | 4 +--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 4a3068edd..6e1de023f 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -414,12 +414,15 @@ end local function do_detached_movement(self, dtime) local staticdata = self._staticdata + -- Make sure the object is still valid before trying to move it + if not self.object or not self.object:get_pos() then return end + -- Apply physics if mcl_physics then mcl_physics.apply_entity_environmental_physics(self) else -- Simple physics - local friction = self.object:get_velocity() + local friction = self.object:get_velocity() or vector.new(0,0,0) friction.y = 0 local accel = vector.new(0,-9.81,0) -- gravity @@ -1322,7 +1325,7 @@ register_minecart({ local pos = self.object:get_pos() if self._boomtimer <= 0 then self.object:remove() - mcl_explosions.explode(pos, 4, { drop_chance = 1.0 }) + mcl_explosions.explode(pos, 6, { drop_chance = 1.0 }) return else tnt.smoke_step(pos) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 45498c89c..17dc19e67 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -156,6 +156,7 @@ table_merge(SLOPED_RAIL_DEF,{ mesh = "sloped_track.obj", groups = { rail_slope = 1, + not_in_creative_inventory = 1, }, collision_box = { type = "fixed", @@ -217,9 +218,6 @@ local function register_straight_rail(base_name, tiles, def) mod.register_rail(base_name, base_def) base_def.craft = nil; sloped_def.craft = nil table_merge(base_def,{ - groups = { - not_in_creative_inventory = 1, - }, _mcl_minecarts = { railtype = "straight", }, From d31839077873c10c87c1b665ef5748cd57b4da7d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 6 Apr 2024 06:50:30 +0000 Subject: [PATCH 060/195] Allow players to push minecarts that are not on track --- mods/ENTITIES/mcl_minecarts/carts.lua | 76 +++++++++++++++++---------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 6e1de023f..8b6c1f767 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -136,34 +136,40 @@ local function update_cart_orientation(self,staticdata) self.object:set_rotation(rot) end -local function direction_away_from_players(self, staticdata) +local function vector_away_from_players(self, staticdata) local objs = minetest.get_objects_inside_radius(self.object:get_pos(), 1.1) for n=1,#objs do local obj = objs[n] local player_name = obj:get_player_name() if player_name and player_name ~= "" and not ( self._driver and self._driver == player_name ) then - local diff = obj:get_pos() - self.object:get_pos() - - local length = vector.distance(vector.new(0,0,0),diff) - local vec = diff / length - local force = vector.dot( vec, vector.normalize(staticdata.dir) ) - - -- Check if this would push past the end of the track and don't move it it would - -- This prevents an oscillation that would otherwise occur - local dir = staticdata.dir - if force > 0 then - dir = -dir - end - if mcl_minecarts:is_rail( staticdata.connected_at + dir ) then - if force > 0.5 then - return -length * 4 - elseif force < -0.5 then - return length * 4 - end - end + return obj:get_pos() - self.object:get_pos() end end + return nil +end + +local function direction_away_from_players(self, staticdata) + local diff = vector_away_from_players(self, staticdata) + if not diff then return 0 end + + local length = vector.distance(vector.new(0,0,0),diff) + local vec = diff / length + local force = vector.dot( vec, vector.normalize(staticdata.dir) ) + + -- Check if this would push past the end of the track and don't move it it would + -- This prevents an oscillation that would otherwise occur + local dir = staticdata.dir + if force > 0 then + dir = -dir + end + if mcl_minecarts:is_rail( staticdata.connected_at + dir ) then + if force > 0.5 then + return -length * 4 + elseif force < -0.5 then + return length * 4 + end + end return 0 end @@ -430,6 +436,12 @@ local function do_detached_movement(self, dtime) self.object:set_acceleration(accel) end + local away = vector_away_from_players(self, staticdata) + if away then + local v = self.object:get_velocity() + self.object:set_velocity(v - away) + end + -- Try to reconnect to rail local pos_r = vector.round(self.object:get_pos()) local node = minetest.get_node(pos_r) @@ -801,21 +813,25 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) return end - local railpos, node + local spawn_pos = pointed_thing.above + local cart_dir = vector.new(1,0,0) + + local railtype, railpos, node if mcl_minecarts:is_rail(pointed_thing.under) then railpos = pointed_thing.under - node = minetest.get_node(pointed_thing.under) elseif mcl_minecarts:is_rail(pointed_thing.above) then railpos = pointed_thing.above - node = minetest.get_node(pointed_thing.above) - else - return + end + if railpos then + spawn_pos = railpos + node = minetest.get_node(railpos) + railtype = minetest.get_item_group(node.name, "connect_to_raillike") + cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0), nil, nil, railtype) end local entity_id = entity_mapping[itemstack:get_name()] - local cart = minetest.add_entity(railpos, entity_id) - local railtype = minetest.get_item_group(node.name, "connect_to_raillike") - local cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0), nil, nil, railtype) + local cart = minetest.add_entity(spawn_pos, entity_id) + cart:set_yaw(minetest.dir_to_yaw(cart_dir)) -- Update static data @@ -829,7 +845,9 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) le._mcl_minecarts_on_place(le, placer) end - handle_cart_enter(le, railpos) + if railpos then + handle_cart_enter(le, railpos) + end local pname = "" if placer then From 8117b9010af9580de08a77802437c73ffde3f5ae Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 6 Apr 2024 07:33:47 +0000 Subject: [PATCH 061/195] Harden against unknown nodes --- mods/ENTITIES/mcl_minecarts/carts.lua | 37 +++++++++++------------ mods/ENTITIES/mcl_minecarts/functions.lua | 4 +-- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 8b6c1f767..f72a33830 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -56,15 +56,16 @@ local function handle_cart_enter_exit(self, pos, next_dir, event) local check_pos = pos + dir * check[1] + right * check[2] + up * check[3] local node = minetest.get_node(check_pos) local node_def = minetest.registered_nodes[node.name] + if node_def then + -- node-specific hook + local hook_name = "_mcl_minecarts_"..event..check[4] + local hook = node_def[hook_name] + if hook then hook(check_pos, self, next_dir, pos) end - -- node-specific hook - local hook_name = "_mcl_minecarts_"..event..check[4] - local hook = node_def[hook_name] - if hook then hook(check_pos, self, next_dir, pos) end - - -- global minecart hook - hook = mcl_minecarts[event..check[4]] - if hook then hook(check_pos, self, next_dir, node_def) end + -- global minecart hook + hook = mcl_minecarts[event..check[4]] + if hook then hook(check_pos, self, next_dir, node_def) end + end end -- Handle cart-specific behaviors @@ -85,9 +86,11 @@ local function handle_cart_node_watches(self, dtime) for _,node_pos in ipairs(watches) do local node = minetest.get_node(node_pos) local node_def = minetest.registered_nodes[node.name] - local hook = node_def._mcl_minecarts_node_on_step - if hook and hook(node_pos, self, dtime) then - new_watches[#new_watches+1] = node_pos + if node_def then + local hook = node_def._mcl_minecarts_node_on_step + if hook and hook(node_pos, self, dtime) then + new_watches[#new_watches+1] = node_pos + end end end @@ -197,10 +200,8 @@ local function calculate_acceleration(self, staticdata) acceleration = 0.6 elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then -- Standard friction - else - if node_def._rail_acceleration then - acceleration = node_def._rail_acceleration * 4 - end + elseif node_def and node_def._rail_acceleration then + acceleration = node_def._rail_acceleration * 4 end -- Factor in gravity after everything else @@ -860,11 +861,9 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) end local function dropper_place_minecart(dropitem, pos) - local node = minetest.get_node(pos) - local nodedef = minetest.registered_nodes[node.name] - -- Don't try to place the minecart if pos isn't a rail - if (nodedef.groups.rail or 0) == 0 then return false end + local node = minetest.get_node(pos) + if minetest.get_item_group(node.name, "rail") == 0 then return false end mcl_minecarts.place_minecart(dropitem, { above = pos, diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index a991793be..0b0d664ce 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -207,8 +207,8 @@ local function update_rail_connections(pos, opt) local node = minetest.get_node(pos) local nodedef = minetest.registered_nodes[node.name] - if not nodedef._mcl_minecarts then - minetest.log("warning", "attemting to rail connect "..node.name) + if not nodedef or not nodedef._mcl_minecarts then + minetest.log("warning", "attemting to rail connect to "..node.name) return end From 09f044245e5d07bdb6898803ca8824ca2d55f959 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 6 Apr 2024 13:40:40 +0000 Subject: [PATCH 062/195] Add utilities to convert between an ObjectRef, it's active object id and a 128bit uuid, move minecart data from entity staticdata to mod storage to eventually allow updating carts when out of range of players and also track what carts are alive, implement on-rail cart collisions --- mods/CORE/mcl_util/init.lua | 46 +++++++++ mods/ENTITIES/mcl_minecarts/carts.lua | 143 ++++++++++++++++++++++++-- mods/ENTITIES/mcl_minecarts/rails.lua | 14 +-- 3 files changed, 183 insertions(+), 20 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 5330948c6..9df10ca7d 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -800,3 +800,49 @@ function mcl_util.table_keys(t) return keys end +local uuid_to_aoid_cache = {} +local function scan_active_objects() + -- Update active object ids for all active objects + for active_object_id,o in pairs(minetest.luaentities) do + o._active_object_id = active_object_id + if o._uuid then + uuid_to_aoid_cache[o._uuid] = active_object_id + end + end +end +function mcl_util.get_active_object_id(obj) + local le = obj:get_luaentity() + + -- If the active object id in the lua entity is correct, return that + if le._active_object_id and minetest.luaentities[le._active_object_id] == le then + return le._active_object_id + end + + scan_active_objects() + + return le._active_object_id +end +function mcl_util.get_active_object_id_from_uuid(uuid) + return uuid_to_aoid_cache[uuid] or scan_active_objects() or uuid_to_aoid_cache[uuid] +end +function mcl_util.get_uuid(obj) + local le = obj:get_luaentity() + + if le._uuid then return le._uuid end + + -- Generate a random 128-bit ID that can be assumed to be unique + -- To have a 1% chance of a collision, there would have to be 1.6x10^76 IDs generated + -- https://en.wikipedia.org/wiki/Birthday_problem#Probability_table + local u = {} + for i = 1,16 do + u[#u + 1] = string.format("%02X",math.random(1,255)) + end + le._uuid = table.concat(u) + + -- Update the cache with this new id + aoid = mcl_util.get_active_object_id(obj) + uuid_to_aoid_cache[le._uuid] = aoid + + return le._uuid +end + diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index f72a33830..7d673e638 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -1,5 +1,6 @@ local modname = minetest.get_current_modname() local mod = mcl_minecarts +local storage = minetest.get_mod_storage() local S = minetest.get_translator(modname) local LOGGING_ON = minetest.settings:get_bool("mcl_logging_minecarts", false) @@ -19,6 +20,32 @@ local friction = 0.4 local MINECART_MAX_HP = 4 local PASSENGER_ATTACH_POSITION = vector.new(0, -1.75, 0) +local cart_data = {} +local cart_data_fail_cache = {} + +local function get_cart_data(uuid) + if cart_data[uuid] then return cart_data[uuid] end + if cart_data_fail_cache[uuid] then return nil end + + local data = minetest.deserialize(storage:get_string("cart-"..uuid)) + if not data then + cart_data_fail_cache[uuid] = true + return nil + end + + cart_data[uuid] = data + return data +end +local function save_cart_data(uuid) + if not cart_data[uuid] then return end + storage:set_string("cart-"..uuid,minetest.serialize(cart_data[uuid])) +end +local function destroy_cart_data(uuid) + storage:set_string("cart-"..uuid,"") + cart_data[uuid] = nil + cart_data_fail_cache[uuid] = true +end + local function detach_minecart(self) local staticdata = self._staticdata @@ -72,13 +99,68 @@ local function handle_cart_enter_exit(self, pos, next_dir, event) local hook = self["_mcl_minecarts_"..event] if hook then hook(self, pos) end end +local function set_metadata_cart_status(pos, uuid, state) + local meta = minetest.get_meta(pos) + local carts = minetest.deserialize(meta:get_string("_mcl_minecarts_carts")) or {} + carts[uuid] = state + meta:set_string("_mcl_minecarts_carts", minetest.serialize(carts)) +end local function handle_cart_enter(self, pos, next_dir) + --print("entering "..tostring(pos)) + set_metadata_cart_status(pos, self._staticdata.uuid, 1) handle_cart_enter_exit(self, pos, next_dir, "on_enter" ) end local function handle_cart_leave(self, pos, next_dir) + --print("leaving "..tostring(pos)) + set_metadata_cart_status(pos, self._staticdata.uuid, nil) handle_cart_enter_exit(self, pos, next_dir, "on_leave" ) end +local function handle_cart_collision(cart1, pos, next_dir) + local meta = minetest.get_meta(pos) + local carts = minetest.deserialize(meta:get_string("_mcl_minecarts_carts")) or {} + local cart_uuid = nil + local dirty = false + for uuid,v in pairs(carts) do + -- Clean up dead carts + if not get_cart_data(uuid) then + carts[uuid] = nil + dirty = true + uuid = nil + end + + if uuid and uuid ~= cart1._staticdata.uuid then cart_uuid = uuid end + end + if dirty then + meta:set_string("_mcl_minecarts_carts",minetest.serialize(carts)) + end + + local meta = minetest.get_meta(vector.add(pos,next_dir)) + if not cart_uuid then return end + minetest.log("action","cart #"..cart1._staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos)) + + local cart2_aoid = mcl_util.get_active_object_id_from_uuid(cart_uuid) + local cart2 = minetest.luaentities[cart2_aoid] + if not cart2 then return end + + local cart1_staticdata = cart1._staticdata + local cart2_staticdata = cart2._staticdata + + local u1 = cart1_staticdata.velocity + local u2 = cart2_staticdata.velocity + local m1 = cart1_staticdata.mass + local m2 = cart2_staticdata.mass + + -- Calculate new velocities according to https://en.wikipedia.org/wiki/Elastic_collision#One-dimensional_Newtonian + local c1 = m1 + m2 + local d = m1 - m2 + local v1 = ( d * u1 + 2 * m2 * u2 ) / c1 + local v2 = ( 2 * m1 * u1 + d * u2 ) / c1 + + cart1_staticdata.velocity = v1 + cart2_staticdata.velocity = v2 +end + local function handle_cart_node_watches(self, dtime) local staticdata = self._staticdata local watches = staticdata.node_watches or {} @@ -246,7 +328,7 @@ local function do_movement_step(self, dtime) end if DEBUG and ( v_0 > 0 or a ~= 0 ) then - print( " cart #"..tostring(staticdata.cart_id).. + print( " cart "..tostring(staticdata.uuid).. ": a="..tostring(a).. ",v_0="..tostring(v_0).. ",x_0="..tostring(x_0).. @@ -305,7 +387,7 @@ local function do_movement_step(self, dtime) staticdata.distance = x_1 if DEBUG and (1==0) and ( v_0 > 0 or a ~= 0 ) then - print( "- cart #"..tostring(staticdata.cart_id).. + print( "- cart #"..tostring(staticdata.uuid).. ": a="..tostring(a).. ",v_0="..tostring(v_0).. ",v_1="..tostring(v_1).. @@ -337,6 +419,9 @@ local function do_movement_step(self, dtime) print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) end + -- Handle cart collisions + handle_cart_collision(self, pos, next_dir) + -- Leave the old node handle_cart_leave(self, old_pos, next_dir ) @@ -376,7 +461,7 @@ local function do_movement_step(self, dtime) -- Debug reporting if DEBUG and ( v_0 > 0 or v_1 > 0 ) then - print( " cart #"..tostring(staticdata.cart_id).. + print( " cart #"..tostring(staticdata.uuid).. ": a="..tostring(a).. ",v_0="..tostring(v_0).. ",v_1="..tostring(v_1).. @@ -615,7 +700,7 @@ local function make_staticdata( railtype, connected_at, dir ) distance = 0, velocity = 0, dir = vector.new(dir), - cart_id = math.random(1,1000000000), + mass = 1, } end @@ -644,24 +729,40 @@ local DEFAULT_CART_DEF = { _staticdata = nil, } function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) + -- Transfer older data + local data = minetest.deserialize(staticdata) or {} + if not data.uuid then + data.uuid = mcl_util.get_uuid(self.object) + end + local cd = get_cart_data(data.uuid) + if not cd then + cart_data[data.uuid] = data + cart_data_fail_cache[data.uuid] = nil + save_cart_data(data.uuid) + end + -- Initialize - local data = minetest.deserialize(staticdata) if type(data) == "table" then -- Migrate old data if data._railtype then data.railtype = data._railtype data._railtype = nil end + -- Fix up types data.dir = vector.new(data.dir) + -- Fix mass + data.mass = data.mass or 1 + -- Make sure all carts have an ID to isolate them - data.cart_id = staticdata.cart_id or math.random(1,1000000000) + self._uuid = data.uuid + data.uuid = mcl_util.get_uuid(self.object) self._staticdata = data end - -- Activate cart if on activator rail + -- Activate cart if on powered activator rail if self.on_activate_by_rail then local pos = self.object:get_pos() local node = minetest.get_node(vector.floor(pos)) @@ -670,6 +771,11 @@ function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) end end end +function DEFAULT_CART_DEF:get_staticdata() + save_cart_data(self._staticdata.uuid) + return minetest.serialize({uuid = self._staticdata.uuid}) +end + function DEFAULT_CART_DEF:add_node_watch(pos) local staticdata = self._staticdata local watches = staticdata.node_watches or {} @@ -693,9 +799,6 @@ function DEFAULT_CART_DEF:remove_node_watch(pos) end staticdata.node_watches = new_watches end -function DEFAULT_CART_DEF:get_staticdata() - return minetest.serialize(self._staticdata or {}) -end function DEFAULT_CART_DEF:on_step(dtime) local staticdata = self._staticdata if not staticdata then @@ -765,11 +868,14 @@ function DEFAULT_CART_DEF:on_step(dtime) local r = 0.6 for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then + self:on_death() + --[[ detach_driver(self) local drop = self.drop for d = 1, #drop do minetest.add_item(pos, drop[d]) end + ]] self.object:remove() return end @@ -777,6 +883,8 @@ function DEFAULT_CART_DEF:on_step(dtime) end function DEFAULT_CART_DEF:on_death(killer) local staticdata = self._staticdata + minetest.log("action", "cart #"..staticdata.uuid.." was killed") + detach_driver(self) -- Detach passenger @@ -792,6 +900,9 @@ function DEFAULT_CART_DEF:on_death(killer) mcl_log("TODO: handle detatched minecart death") end + -- Remove data + destroy_cart_data(staticdata.uuid) + -- Drop items local drop = self.drop if not killer or not minetest.is_creative_enabled(killer:get_player_name()) then @@ -838,7 +949,12 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) -- Update static data local le = cart:get_luaentity() if le then - le._staticdata = make_staticdata( railtype, railpos, cart_dir ) + local uuid = mcl_util.get_uuid(cart) + data = make_staticdata( railtype, railpos, cart_dir ) + data.uuid = uuid + cart_data[uuid] = data + le._staticdata = data + save_cart_data(le._staticdata.uuid) end -- Call placer @@ -1386,3 +1502,8 @@ if minetest.get_modpath("mcl_wip") then mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart") mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart") end + +minetest.register_globalstep(function(dtime) + -- TODO: handle periodically updating out-of-range carts +end) + diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 17dc19e67..d0a2eaa4a 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -177,7 +177,7 @@ table_merge(SLOPED_RAIL_DEF,{ }, }) -local function register_rail_v2(itemstring, ndef) +function mod.register_rail(itemstring, ndef) assert(ndef.tiles) -- Extract out the craft recipe @@ -188,15 +188,14 @@ local function register_rail_v2(itemstring, ndef) if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end - --print("registering rail "..itemstring.." with definition: "..dump(ndef)) + print("registering rail "..itemstring.." with definition: "..dump(ndef)) -- Make registrations minetest.register_node(itemstring, ndef) if craft then minetest.register_craft(craft) end end -mod.register_rail = register_rail_v2 -local function register_straight_rail(base_name, tiles, def) +function mod.register_straight_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) local sloped_def = table.copy(SLOPED_RAIL_DEF) @@ -235,9 +234,8 @@ local function register_straight_rail(base_name, tiles, def) }, })) end -mod.register_straight_rail = register_straight_rail -local function register_curves_rail(base_name, tiles, def) +function mod.register_curves_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) local sloped_def = table.copy(SLOPED_RAIL_DEF) @@ -333,9 +331,8 @@ local function register_curves_rail(base_name, tiles, def) }, })) end -mod.register_curves_rail = register_curves_rail -local function register_rail_sloped(itemstring, def) +function mod.register_rail_sloped(itemstring, def) assert(def.tiles) -- Build the node definition @@ -351,7 +348,6 @@ local function register_rail_sloped(itemstring, def) -- Make registrations minetest.register_node(itemstring, ndef) end -mod.register_rail_sloped = register_rail_sloped -- Redstone rules local rail_rules_long = From 1b9c149803b858fcf29ab2b2c64f4abebed307a2 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 6 Apr 2024 14:23:59 +0000 Subject: [PATCH 063/195] Make sure carts that collide move in the same direction the colliding cart was --- mods/ENTITIES/mcl_minecarts/carts.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 7d673e638..acd4aced5 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -116,7 +116,10 @@ local function handle_cart_leave(self, pos, next_dir) handle_cart_enter_exit(self, pos, next_dir, "on_leave" ) end -local function handle_cart_collision(cart1, pos, next_dir) +local function handle_cart_collision(cart1, prev_pos, next_dir) + -- Look ahead one block + local pos = vector.add(prev_pos, next_dir) + local meta = minetest.get_meta(pos) local carts = minetest.deserialize(meta:get_string("_mcl_minecarts_carts")) or {} local cart_uuid = nil @@ -159,6 +162,9 @@ local function handle_cart_collision(cart1, pos, next_dir) cart1_staticdata.velocity = v1 cart2_staticdata.velocity = v2 + + -- Force the other cart to move the same direction this one was + cart2_staticdata.dir = cart1_staticdata.dir end local function handle_cart_node_watches(self, dtime) From dbeec9b542b9023c63e0260262232ced8d8c877b Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 6 Apr 2024 17:09:54 +0000 Subject: [PATCH 064/195] Major reorganization, start setup for trains --- mods/ENTITIES/mcl_minecarts/carts.lua | 1103 +---------------- .../ENTITIES/mcl_minecarts/carts/minecart.lua | 84 ++ .../mcl_minecarts/carts/with_chest.lua | 35 + .../mcl_minecarts/carts/with_commandblock.lua | 64 + .../mcl_minecarts/carts/with_furnace.lua | 92 ++ .../mcl_minecarts/carts/with_hopper.lua | 178 +++ .../ENTITIES/mcl_minecarts/carts/with_tnt.lua | 123 ++ mods/ENTITIES/mcl_minecarts/functions.lua | 45 + mods/ENTITIES/mcl_minecarts/init.lua | 13 +- mods/ENTITIES/mcl_minecarts/movement.lua | 496 ++++++++ mods/ENTITIES/mcl_minecarts/rails.lua | 2 +- mods/ENTITIES/mcl_minecarts/storage.lua | 37 + mods/ENTITIES/mcl_minecarts/train.lua | 5 + 13 files changed, 1188 insertions(+), 1089 deletions(-) create mode 100644 mods/ENTITIES/mcl_minecarts/carts/minecart.lua create mode 100644 mods/ENTITIES/mcl_minecarts/carts/with_chest.lua create mode 100644 mods/ENTITIES/mcl_minecarts/carts/with_commandblock.lua create mode 100644 mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua create mode 100644 mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua create mode 100644 mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua create mode 100644 mods/ENTITIES/mcl_minecarts/movement.lua create mode 100644 mods/ENTITIES/mcl_minecarts/storage.lua create mode 100644 mods/ENTITIES/mcl_minecarts/train.lua diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index acd4aced5..a0af8d64c 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -1,6 +1,6 @@ local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts -local storage = minetest.get_mod_storage() local S = minetest.get_translator(modname) local LOGGING_ON = minetest.settings:get_bool("mcl_logging_minecarts", false) @@ -11,41 +11,22 @@ local function mcl_log(message) end end --- Import external functions +-- Imports local table_merge = mcl_util.table_merge +local get_cart_data = mod.get_cart_data +local save_cart_data = mod.save_cart_data +local update_cart_data = mod.update_cart_data +local destroy_cart_data = mod.destroy_cart_data +local do_movement,do_detached_movement,handle_cart_enter = dofile(modpath.."/movement.lua") +assert(do_movement) +assert(do_detached_movement) +assert(handle_cart_enter) -- Constants local max_step_distance = 0.5 -local friction = 0.4 local MINECART_MAX_HP = 4 local PASSENGER_ATTACH_POSITION = vector.new(0, -1.75, 0) -local cart_data = {} -local cart_data_fail_cache = {} - -local function get_cart_data(uuid) - if cart_data[uuid] then return cart_data[uuid] end - if cart_data_fail_cache[uuid] then return nil end - - local data = minetest.deserialize(storage:get_string("cart-"..uuid)) - if not data then - cart_data_fail_cache[uuid] = true - return nil - end - - cart_data[uuid] = data - return data -end -local function save_cart_data(uuid) - if not cart_data[uuid] then return end - storage:set_string("cart-"..uuid,minetest.serialize(cart_data[uuid])) -end -local function destroy_cart_data(uuid) - storage:set_string("cart-"..uuid,"") - cart_data[uuid] = nil - cart_data_fail_cache[uuid] = true -end - local function detach_minecart(self) local staticdata = self._staticdata @@ -61,499 +42,6 @@ local function try_detach_minecart(self) end end ---[[ - Array of hooks { {u,v,w}, name } - Actual position is pos + u * dir + v * right + w * up -]] -local enter_exit_checks = { - { 0, 0, 0, "" }, - { 0, 0, 1, "_above" }, - { 0, 0,-1, "_below" }, - { 0, 1, 0, "_side" }, - { 0,-1, 0, "_side" }, -} - -local function handle_cart_enter_exit(self, pos, next_dir, event) - local staticdata = self._staticdata - - local dir = staticdata.dir - local right = vector.new( dir.z, dir.y, -dir.x) - local up = vector.new(0,1,0) - for _,check in ipairs(enter_exit_checks) do - local check_pos = pos + dir * check[1] + right * check[2] + up * check[3] - local node = minetest.get_node(check_pos) - local node_def = minetest.registered_nodes[node.name] - if node_def then - -- node-specific hook - local hook_name = "_mcl_minecarts_"..event..check[4] - local hook = node_def[hook_name] - if hook then hook(check_pos, self, next_dir, pos) end - - -- global minecart hook - hook = mcl_minecarts[event..check[4]] - if hook then hook(check_pos, self, next_dir, node_def) end - end - end - - -- Handle cart-specific behaviors - local hook = self["_mcl_minecarts_"..event] - if hook then hook(self, pos) end -end -local function set_metadata_cart_status(pos, uuid, state) - local meta = minetest.get_meta(pos) - local carts = minetest.deserialize(meta:get_string("_mcl_minecarts_carts")) or {} - carts[uuid] = state - meta:set_string("_mcl_minecarts_carts", minetest.serialize(carts)) -end -local function handle_cart_enter(self, pos, next_dir) - --print("entering "..tostring(pos)) - set_metadata_cart_status(pos, self._staticdata.uuid, 1) - handle_cart_enter_exit(self, pos, next_dir, "on_enter" ) -end -local function handle_cart_leave(self, pos, next_dir) - --print("leaving "..tostring(pos)) - set_metadata_cart_status(pos, self._staticdata.uuid, nil) - handle_cart_enter_exit(self, pos, next_dir, "on_leave" ) -end - -local function handle_cart_collision(cart1, prev_pos, next_dir) - -- Look ahead one block - local pos = vector.add(prev_pos, next_dir) - - local meta = minetest.get_meta(pos) - local carts = minetest.deserialize(meta:get_string("_mcl_minecarts_carts")) or {} - local cart_uuid = nil - local dirty = false - for uuid,v in pairs(carts) do - -- Clean up dead carts - if not get_cart_data(uuid) then - carts[uuid] = nil - dirty = true - uuid = nil - end - - if uuid and uuid ~= cart1._staticdata.uuid then cart_uuid = uuid end - end - if dirty then - meta:set_string("_mcl_minecarts_carts",minetest.serialize(carts)) - end - - local meta = minetest.get_meta(vector.add(pos,next_dir)) - if not cart_uuid then return end - minetest.log("action","cart #"..cart1._staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos)) - - local cart2_aoid = mcl_util.get_active_object_id_from_uuid(cart_uuid) - local cart2 = minetest.luaentities[cart2_aoid] - if not cart2 then return end - - local cart1_staticdata = cart1._staticdata - local cart2_staticdata = cart2._staticdata - - local u1 = cart1_staticdata.velocity - local u2 = cart2_staticdata.velocity - local m1 = cart1_staticdata.mass - local m2 = cart2_staticdata.mass - - -- Calculate new velocities according to https://en.wikipedia.org/wiki/Elastic_collision#One-dimensional_Newtonian - local c1 = m1 + m2 - local d = m1 - m2 - local v1 = ( d * u1 + 2 * m2 * u2 ) / c1 - local v2 = ( 2 * m1 * u1 + d * u2 ) / c1 - - cart1_staticdata.velocity = v1 - cart2_staticdata.velocity = v2 - - -- Force the other cart to move the same direction this one was - cart2_staticdata.dir = cart1_staticdata.dir -end - -local function handle_cart_node_watches(self, dtime) - local staticdata = self._staticdata - local watches = staticdata.node_watches or {} - local new_watches = {} - for _,node_pos in ipairs(watches) do - local node = minetest.get_node(node_pos) - local node_def = minetest.registered_nodes[node.name] - if node_def then - local hook = node_def._mcl_minecarts_node_on_step - if hook and hook(node_pos, self, dtime) then - new_watches[#new_watches+1] = node_pos - end - end - end - - staticdata.node_watches = new_watches -end - -local function update_cart_orientation(self,staticdata) - -- constants - local _2_pi = math.pi * 2 - local pi = math.pi - local dir = staticdata.dir - - -- Calculate an angle from the x,z direction components - local rot_y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) - if rot_y < 0 then - rot_y = rot_y + _2_pi - end - - -- Check if the rotation is a 180 flip and don't change if so - local rot = self.object:get_rotation() - local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) - if diff < 0.001 or diff > _2_pi - 0.001 then - -- Update rotation adjust and recalculate the rotation - staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi - rot.y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) - else - rot.y = rot_y - end - - -- Forward/backwards tilt (pitch) - if dir.y < 0 then - rot.x = -0.25 * pi - elseif dir.y > 0 then - rot.x = 0.25 * pi - else - rot.x = 0 - end - - if ( staticdata.rot_adjust or 0 ) < 0.01 then - rot.x = -rot.x - end - if dir.z ~= 0 then - rot.x = -rot.x - end - - self.object:set_rotation(rot) -end - -local function vector_away_from_players(self, staticdata) - local objs = minetest.get_objects_inside_radius(self.object:get_pos(), 1.1) - for n=1,#objs do - local obj = objs[n] - local player_name = obj:get_player_name() - if player_name and player_name ~= "" and not ( self._driver and self._driver == player_name ) then - return obj:get_pos() - self.object:get_pos() - end - end - - return nil -end - -local function direction_away_from_players(self, staticdata) - local diff = vector_away_from_players(self, staticdata) - if not diff then return 0 end - - local length = vector.distance(vector.new(0,0,0),diff) - local vec = diff / length - local force = vector.dot( vec, vector.normalize(staticdata.dir) ) - - -- Check if this would push past the end of the track and don't move it it would - -- This prevents an oscillation that would otherwise occur - local dir = staticdata.dir - if force > 0 then - dir = -dir - end - if mcl_minecarts:is_rail( staticdata.connected_at + dir ) then - if force > 0.5 then - return -length * 4 - elseif force < -0.5 then - return length * 4 - end - end - return 0 -end - -local function calculate_acceleration(self, staticdata) - local acceleration = 0 - - -- Fix up movement data - staticdata.velocity = staticdata.velocity or 0 - - -- Apply friction if moving - if staticdata.velocity > 0 then - acceleration = -friction - end - - local pos = staticdata.connected_at - local node_name = minetest.get_node(pos).name - local node_def = minetest.registered_nodes[node_name] - local max_vel = mcl_minecarts.speed_max - - if self._go_forward then - acceleration = 4 - elseif self._brake then - acceleration = -1.5 - elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then - acceleration = 0.6 - elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then - -- Standard friction - elseif node_def and node_def._rail_acceleration then - acceleration = node_def._rail_acceleration * 4 - end - - -- Factor in gravity after everything else - local gravity_strength = 2.45 --friction * 5 - if staticdata.dir.y < 0 then - acceleration = gravity_strength - friction - elseif staticdata.dir.y > 0 then - acceleration = -gravity_strength + friction - end - - return acceleration -end - -local function reverse_direction(self, staticdata) - -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at - local next_dir = -staticdata.dir - staticdata.connected_at = staticdata.connected_at + staticdata.dir - staticdata.distance = 1 - staticdata.distance - - -- recalculate direction - local next_dir,_ = mcl_minecarts:get_rail_direction(staticdata.connected_at, next_dir, nil, nil, staticdata.railtype) - staticdata.dir = next_dir -end - -local function do_movement_step(self, dtime) - local staticdata = self._staticdata - if not staticdata.connected_at then return 0 end - - -- Calculate timestep remaiing in this block - local x_0 = staticdata.distance or 0 - local remaining_in_block = 1 - x_0 - local a = calculate_acceleration(self, staticdata) - local v_0 = staticdata.velocity - - -- Repel minecarts - local away = direction_away_from_players(self, staticdata) - if away > 0 then - v_0 = away - elseif away < 0 then - reverse_direction(self, staticdata) - v_0 = -away - end - - if DEBUG and ( v_0 > 0 or a ~= 0 ) then - print( " cart "..tostring(staticdata.uuid).. - ": a="..tostring(a).. - ",v_0="..tostring(v_0).. - ",x_0="..tostring(x_0).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir).. - ",connected_at="..tostring(staticdata.connected_at).. - ",distance="..tostring(staticdata.distance) - ) - end - - -- Not moving - if a == 0 and v_0 == 0 then return 0 end - - -- Movement equation with acceleration: x_1 = x_0 + v_0 * t + 0.5 * a * t*t - local timestep - local stops_in_block = false - local inside = v_0 * v_0 + 2 * a * remaining_in_block - if inside < 0 then - -- Would stop or reverse direction inside this block, calculate time to v_1 = 0 - timestep = -v_0 / a - stops_in_block = true - elseif a ~= 0 then - -- Setting x_1 = x_0 + remaining_in_block, and solving for t gives: - timestep = ( math.sqrt( v_0 * v_0 + 2 * a * remaining_in_block) - v_0 ) / a - else - timestep = remaining_in_block / v_0 - end - - -- Truncate timestep to remaining time delta - if timestep > dtime then - timestep = dtime - end - - -- Truncate timestep to prevent v_1 from being larger that speed_max - local v_max = mcl_minecarts.speed_max - if (v_0 ~= v_max) and ( v_0 + a * timestep > v_max) then - timestep = ( v_max - v_0 ) / a - end - - -- Prevent infinite loops - if timestep <= 0 then return 0 end - - -- Calculate v_1 taking v_max into account - local v_1 = v_0 + a * timestep - if v_1 > v_max then - v_1 = v_max - elseif v_1 < friction / 5 then - v_1 = 0 - end - - -- Calculate x_1 - local x_1 = x_0 + timestep * v_0 + 0.5 * a * timestep * timestep - - -- Update position and velocity of the minecart - staticdata.velocity = v_1 - staticdata.distance = x_1 - - if DEBUG and (1==0) and ( v_0 > 0 or a ~= 0 ) then - print( "- cart #"..tostring(staticdata.uuid).. - ": a="..tostring(a).. - ",v_0="..tostring(v_0).. - ",v_1="..tostring(v_1).. - ",x_0="..tostring(x_0).. - ",x_1="..tostring(x_1).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir).. - ",connected_at="..tostring(staticdata.connected_at).. - ",distance="..tostring(staticdata.distance) - ) - end - - - -- Entity movement - local pos = staticdata.connected_at - - -- Handle movement to next block, account for loss of precision in calculations - if x_1 >= 0.99 then - staticdata.distance = 0 - - -- Anchor at the next node - local old_pos = pos - pos = pos + staticdata.dir - staticdata.connected_at = pos - - -- Get the next direction - local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) - if DEBUG and next_dir ~= staticdata.dir then - print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) - end - - -- Handle cart collisions - handle_cart_collision(self, pos, next_dir) - - -- Leave the old node - handle_cart_leave(self, old_pos, next_dir ) - - -- Enter the new node - handle_cart_enter(self, pos, next_dir) - - try_detach_minecart(self) - - -- Handle end of track - if next_dir == staticdata.dir * -1 and next_dir.y == 0 then - if DEBUG then print("Stopping cart at end of track at "..tostring(pos)) end - staticdata.velocity = 0 - end - - -- Update cart direction - staticdata.dir = next_dir - elseif stops_in_block and v_1 < (friction/5) and a <= 0 then - -- Handle direction flip due to gravity - if DEBUG then print("Gravity flipped direction") end - - -- Velocity should be zero at this point - staticdata.velocity = 0 - - reverse_direction(self, staticdata) - - -- Intermediate movement - pos = staticdata.connected_at + staticdata.dir * staticdata.distance - else - -- Intermediate movement - pos = pos + staticdata.dir * staticdata.distance - end - - self.object:move_to(pos) - - -- Update cart orientation - update_cart_orientation(self,staticdata) - - -- Debug reporting - if DEBUG and ( v_0 > 0 or v_1 > 0 ) then - print( " cart #"..tostring(staticdata.uuid).. - ": a="..tostring(a).. - ",v_0="..tostring(v_0).. - ",v_1="..tostring(v_1).. - ",x_0="..tostring(x_0).. - ",x_1="..tostring(x_1).. - ",timestep="..tostring(timestep).. - ",dir="..tostring(staticdata.dir).. - ",pos="..tostring(pos).. - ",connected_at="..tostring(staticdata.connected_at).. - ",distance="..tostring(staticdata.distance) - ) - end - - -- Report the amount of time processed - return dtime - timestep -end - -local function do_movement( self, dtime ) - local staticdata = self._staticdata - - -- Allow the carts to be delay for the rest of the world to react before moving again - if ( staticdata.delay or 0 ) > dtime then - staticdata.delay = staticdata.delay - dtime - return - else - staticdata.delay = 0 - end - - -- Break long movements at block boundaries to make it - -- it impossible to jump across gaps due to server lag - -- causing large timesteps - while dtime > 0 do - local new_dtime = do_movement_step(self, dtime) - - -- Handle node watches here in steps to prevent server lag from changing behavior - handle_cart_node_watches(self, dtime - new_dtime) - - dtime = new_dtime - end -end - -local function do_detached_movement(self, dtime) - local staticdata = self._staticdata - - -- Make sure the object is still valid before trying to move it - if not self.object or not self.object:get_pos() then return end - - -- Apply physics - if mcl_physics then - mcl_physics.apply_entity_environmental_physics(self) - else - -- Simple physics - local friction = self.object:get_velocity() or vector.new(0,0,0) - friction.y = 0 - - local accel = vector.new(0,-9.81,0) -- gravity - accel = vector.add(accel, vector.multiply(friction,-0.9)) - self.object:set_acceleration(accel) - end - - local away = vector_away_from_players(self, staticdata) - if away then - local v = self.object:get_velocity() - self.object:set_velocity(v - away) - end - - -- Try to reconnect to rail - local pos_r = vector.round(self.object:get_pos()) - local node = minetest.get_node(pos_r) - if minetest.get_item_group(node.name, "rail") ~= 0 then - staticdata.connected_at = pos_r - staticdata.railtype = node.name - - local freebody_velocity = self.object:get_velocity() - staticdata.dir = mod:get_rail_direction(pos_r, mod.snap_direction(freebody_velocity)) - - -- Use vector projection to only keep the velocity in the new direction of movement on the rail - -- https://en.wikipedia.org/wiki/Vector_projection - staticdata.velocity = vector.dot(staticdata.dir,freebody_velocity) - - -- Clear freebody movement - self.object:set_velocity(vector.new(0,0,0)) - self.object:set_acceleration(vector.new(0,0,0)) - end -end - local function detach_driver(self) if not self._driver then return @@ -569,133 +57,6 @@ local function detach_driver(self) end end -local function activate_normal_minecart(self) - detach_driver(self) - - -- Detach passenger - if self._passenger then - local mob = self._passenger.object - mob:set_detach() - end -end - -local function hopper_take_item(self, dtime) - local pos = self.object:get_pos() - if not pos then return end - - if not self or self.name ~= "mcl_minecarts:hopper_minecart" then return end - - if mcl_util.check_dtime_timer(self, dtime, "hoppermc_take", 0.15) then - --minetest.log("The check timer was triggered: " .. dump(pos) .. ", name:" .. self.name) - else - --minetest.log("The check timer was not triggered") - return - end - - - local above_pos = vector.offset(pos, 0, 0.9, 0) - local objs = minetest.get_objects_inside_radius(above_pos, 1.25) - - if objs then - mcl_log("there is an itemstring. Number of objs: ".. #objs) - - for k, v in pairs(objs) do - local ent = v:get_luaentity() - - if ent and not ent._removed and ent.itemstring and ent.itemstring ~= "" then - local taken_items = false - - mcl_log("ent.name: " .. tostring(ent.name)) - mcl_log("ent pos: " .. tostring(ent.object:get_pos())) - - local inv = mcl_entity_invs.load_inv(self, 5) - if not inv then return false end - - local current_itemstack = ItemStack(ent.itemstring) - - mcl_log("inv. size: " .. self._inv_size) - if inv:room_for_item("main", current_itemstack) then - mcl_log("Room") - inv:add_item("main", current_itemstack) - ent.object:get_luaentity().itemstring = "" - ent.object:remove() - taken_items = true - else - mcl_log("no Room") - end - - if not taken_items then - local items_remaining = current_itemstack:get_count() - - -- This will take part of a floating item stack if no slot can hold the full amount - for i = 1, self._inv_size, 1 do - local stack = inv:get_stack("main", i) - - mcl_log("i: " .. tostring(i)) - mcl_log("Items remaining: " .. items_remaining) - mcl_log("Name: " .. tostring(stack:get_name())) - - if current_itemstack:get_name() == stack:get_name() then - mcl_log("We have a match. Name: " .. tostring(stack:get_name())) - - local room_for = stack:get_stack_max() - stack:get_count() - mcl_log("Room for: " .. tostring(room_for)) - - if room_for == 0 then - -- Do nothing - mcl_log("No room") - elseif room_for < items_remaining then - mcl_log("We have more items remaining than space") - - items_remaining = items_remaining - room_for - stack:set_count(stack:get_stack_max()) - inv:set_stack("main", i, stack) - taken_items = true - else - local new_stack_size = stack:get_count() + items_remaining - stack:set_count(new_stack_size) - mcl_log("We have more than enough space. Now holds: " .. new_stack_size) - - inv:set_stack("main", i, stack) - items_remaining = 0 - - ent.object:get_luaentity().itemstring = "" - ent.object:remove() - - taken_items = true - break - end - - mcl_log("Count: " .. tostring(stack:get_count())) - mcl_log("stack max: " .. tostring(stack:get_stack_max())) - --mcl_log("Is it empty: " .. stack:to_string()) - end - - if i == self._inv_size and taken_items then - mcl_log("We are on last item and still have items left. Set final stack size: " .. items_remaining) - current_itemstack:set_count(items_remaining) - --mcl_log("Itemstack2: " .. current_itemstack:to_string()) - ent.itemstring = current_itemstack:to_string() - end - end - end - - --Add in, and delete - if taken_items then - mcl_log("Saving") - mcl_entity_invs.save_inv(ent) - return taken_items - else - mcl_log("No need to save") - end - - end - end - end - - return false -end - -- Table for item-to-entity mapping. Keys: itemstring, Values: Corresponding entity ID local entity_mapping = {} @@ -741,11 +102,7 @@ function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) data.uuid = mcl_util.get_uuid(self.object) end local cd = get_cart_data(data.uuid) - if not cd then - cart_data[data.uuid] = data - cart_data_fail_cache[data.uuid] = nil - save_cart_data(data.uuid) - end + if not cd then update_cart_data(data) end -- Initialize if type(data) == "table" then @@ -875,13 +232,6 @@ function DEFAULT_CART_DEF:on_step(dtime) for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then self:on_death() - --[[ - detach_driver(self) - local drop = self.drop - for d = 1, #drop do - minetest.add_item(pos, drop[d]) - end - ]] self.object:remove() return end @@ -958,7 +308,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) local uuid = mcl_util.get_uuid(cart) data = make_staticdata( railtype, railpos, cart_dir ) data.uuid = uuid - cart_data[uuid] = data + update_cart_data(data) le._staticdata = data save_cart_data(le._staticdata.uuid) end @@ -1056,7 +406,7 @@ Register a minecart * on_activate_by_rail: Called when above activator rail * creative: If false, don't show in Creative Inventory ]] -local function register_minecart(def) +function mcl_minecarts.register_minecart(def) assert( def.drop, "def.drop is required parameter" ) assert( def.itemstring, "def.itemstring is required parameter" ) @@ -1081,427 +431,14 @@ local function register_minecart(def) minetest.register_craft(craft) end end +local register_minecart = mcl_minecarts.register_minecart --- Minecart -register_minecart({ - itemstring = "mcl_minecarts:minecart", - craft = { - output = "mcl_minecarts:minecart", - recipe = { - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot"}, - }, - }, - entity_id = "mcl_minecarts:minecart", - description = S("Minecart"), - tt_helop = S("Vehicle for fast travel on rails"), - long_descp = S("Minecarts can be used for a quick transportion on rails.") .. "\n" .. - S("Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type."), - S("You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.") .. "\n" .. - S("To obtain the minecart, punch it while holding down the sneak key.") .. "\n" .. - S("If it moves over a powered activator rail, you'll get ejected."), - initial_properties = { - mesh = "mcl_minecarts_minecart.b3d", - textures = {"mcl_minecarts_minecart.png"}, - }, - icon = "mcl_minecarts_minecart_normal.png", - drop = {"mcl_minecarts:minecart"}, - on_rightclick = function(self, clicker) - local name = clicker:get_player_name() - if not clicker or not clicker:is_player() then - return - end - local player_name = clicker:get_player_name() - if self._driver and player_name == self._driver then - --detach_driver(self) - elseif not self._driver and not clicker:get_player_control().sneak then - self._driver = player_name - self._start_pos = self.object:get_pos() - mcl_player.player_attached[player_name] = true - clicker:set_attach(self.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) - mcl_player.player_attached[name] = true - minetest.after(0.2, function(name) - local player = minetest.get_player_by_name(name) - if player then - mcl_player.player_set_animation(player, "sit" , 30) - player:set_eye_offset(vector.new(0,-5.5,0), vector.new(0,-4,0)) - mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) - end - end, name) - end - end, - on_activate_by_rail = activate_normal_minecart, - _mcl_minecarts_on_step = function(self, dtime) - -- Grab mob - if math.random(1,20) > 15 and not self._passenger then - local mobsnear = minetest.get_objects_inside_radius(self.object:get_pos(), 1.3) - for n=1, #mobsnear do - local mob = mobsnear[n] - if mob then - local entity = mob:get_luaentity() - if entity and entity.is_mob then - self._passenger = entity - mob:set_attach(self.object, "", PASSENGER_ATTACH_POSITION, vector.zero()) - break - end - end - end - elseif self._passenger then - local passenger_pos = self._passenger.object:get_pos() - if not passenger_pos then - self._passenger = nil - end - end - end -}) - --- Minecart with Chest -register_minecart({ - itemstring = "mcl_minecarts:chest_minecart", - craft = { - output = "mcl_minecarts:chest_minecart", - recipe = { - {"mcl_chests:chest"}, - {"mcl_minecarts:minecart"}, - }, - }, - entity_id = "mcl_minecarts:chest_minecart", - description = S("Minecart with Chest"), - tt_help = nil, - longdesc = nil, - usagehelp = nil, - initial_properties = { - mesh = "mcl_minecarts_minecart_chest.b3d", - textures = { - "mcl_chests_normal.png", - "mcl_minecarts_minecart.png" - }, - }, - icon = "mcl_minecarts_minecart_chest.png", - drop = {"mcl_minecarts:minecart", "mcl_chests:chest"}, - groups = { container = 1 }, - on_rightclick = nil, - on_activate_by_rail = nil, - creative = true -}) -mcl_entity_invs.register_inv("mcl_minecarts:chest_minecart","Minecart",27,false,true) - --- Minecart with Furnace -register_minecart({ - itemstring = "mcl_minecarts:furnace_minecart", - craft = { - output = "mcl_minecarts:furnace_minecart", - recipe = { - {"mcl_furnaces:furnace"}, - {"mcl_minecarts:minecart"}, - }, - }, - entity_id = "mcl_minecarts:furnace_minecart", - description = S("Minecart with Furnace"), - tt_help = nil, - longdesc = S("A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel."), - usagehelp = S("Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.") .. "\n" .. - S("To obtain the minecart and furnace, punch them while holding down the sneak key."), - - initial_properties = { - mesh = "mcl_minecarts_minecart_block.b3d", - textures = { - "default_furnace_top.png", - "default_furnace_top.png", - "default_furnace_front.png", - "default_furnace_side.png", - "default_furnace_side.png", - "default_furnace_side.png", - "mcl_minecarts_minecart.png", - }, - }, - icon = "mcl_minecarts_minecart_furnace.png", - drop = {"mcl_minecarts:minecart", "mcl_furnaces:furnace"}, - on_rightclick = function(self, clicker) - local staticdata = self._staticdata - - -- Feed furnace with coal - if not clicker or not clicker:is_player() then - return - end - local held = clicker:get_wielded_item() - if minetest.get_item_group(held:get_name(), "coal") == 1 then - staticdata.fueltime = (staticdata.fueltime or 0) + 180 - - -- Trucate to 27 minutes (9 uses) - if staticdata.fueltime > 27*60 then - staticdata.fuel_time = 27*60 - end - - if not minetest.is_creative_enabled(clicker:get_player_name()) then - held:take_item() - local index = clicker:get_wield_index() - local inv = clicker:get_inventory() - inv:set_stack("main", index, held) - end - self.object:set_properties({textures = - { - "default_furnace_top.png", - "default_furnace_top.png", - "default_furnace_front_active.png", - "default_furnace_side.png", - "default_furnace_side.png", - "default_furnace_side.png", - "mcl_minecarts_minecart.png", - }}) - end - end, - on_activate_by_rail = nil, - creative = true, - _mcl_minecarts_on_step = function(self, dtime) - local staticdata = self._staticdata - - -- Update furnace stuff - if (staticdata.fueltime or 0) > 0 then - staticdata.fueltime = (staticdata.fueltime or dtime) - dtime - if staticdata.fueltime <= 0 then - self.object:set_properties({textures = - { - "default_furnace_top.png", - "default_furnace_top.png", - "default_furnace_front.png", - "default_furnace_side.png", - "default_furnace_side.png", - "default_furnace_side.png", - "mcl_minecarts_minecart.png", - }}) - staticdata.fueltime = 0 - end - end - end -}) -function table_metadata(table) - return { - table = table, - set_string = function(self, key, value) - --print("set_string("..tostring(key)..", "..tostring(value)..")") - self.table[key] = tostring(value) - end, - get_string = function(self, key) - if self.table[key] then - return tostring(self.table[key]) - end - end - } -end - --- Minecart with Command Block -register_minecart({ - itemstring = "mcl_minecarts:command_block_minecart", - entity_id = "mcl_minecarts:command_block_minecart", - description = S("Minecart with Command Block"), - tt_help = nil, - loncdesc = nil, - usagehelp = nil, - initial_properties = { - mesh = "mcl_minecarts_minecart_block.b3d", - textures = { - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "jeija_commandblock_off.png^[verticalframe:2:0", - "mcl_minecarts_minecart.png", - }, - }, - icon = "mcl_minecarts_minecart_command_block.png", - drop = {"mcl_minecarts:minecart"}, - on_rightclick = function(self, clicker) - self._staticdata.meta = self._staticdata.meta or {} - local meta = table_metadata(self._staticdata.meta) - - mesecon.commandblock.handle_rightclick(meta, clicker) - end, - _mcl_minecarts_on_place = function(self, placer) - -- Create a fake metadata object that stores into the cart's staticdata - self._staticdata.meta = self._staticdata.meta or {} - local meta = table_metadata(self._staticdata.meta) - - mesecon.commandblock.initialize(meta) - mesecon.commandblock.place(meta, placer) - end, - on_activate_by_rail = function(self, timer) - self._staticdata.meta = self._staticdata.meta or {} - local meta = table_metadata(self._staticdata.meta) - - mesecon.commandblock.action_on(meta, self.object:get_pos()) - end, - creative = true -}) - --- Minecart with Hopper -register_minecart({ - itemstring = "mcl_minecarts:hopper_minecart", - craft = { - output = "mcl_minecarts:hopper_minecart", - recipe = { - {"mcl_hoppers:hopper"}, - {"mcl_minecarts:minecart"}, - }, - }, - entity_id = "mcl_minecarts:hopper_minecart", - description = S("Minecart with Hopper"), - tt_help = nil, - longdesc = nil, - usagehelp = nil, - initial_properties = { - mesh = "mcl_minecarts_minecart_hopper.b3d", - textures = { - "mcl_hoppers_hopper_inside.png", - "mcl_minecarts_minecart.png", - "mcl_hoppers_hopper_outside.png", - "mcl_hoppers_hopper_top.png", - }, - }, - icon = "mcl_minecarts_minecart_hopper.png", - drop = {"mcl_minecarts:minecart", "mcl_hoppers:hopper"}, - groups = { container = 1 }, - on_rightclick = nil, - on_activate_by_rail = nil, - _mcl_minecarts_on_enter = function(self, pos) - local staticdata = self._staticdata - if (staticdata.hopper_delay or 0) > 0 then - return - end - - -- try to pull from containers into our inventory - local inv = mcl_entity_invs.load_inv(self,5) - local above_pos = pos + vector.new(0,1,0) - mcl_util.hopper_pull_to_inventory(inv, 'main', above_pos, pos) - - staticdata.hopper_delay = (staticdata.hopper_delay or 0) + (1/20) - end, - _mcl_minecarts_on_step = function(self, dtime) - hopper_take_item(self, dtime) - end, - creative = true -}) -mcl_entity_invs.register_inv("mcl_minecarts:hopper_minecart", "Hopper Minecart", 5, false, true) - --- Minecart with TNT -local function activate_tnt_minecart(self, timer) - if self._boomtimer then - return - end - if timer then - self._boomtimer = timer - else - self._boomtimer = tnt.BOOMTIMER - end - self.object:set_properties({textures = { - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_minecarts_minecart.png", - }}) - self._blinktimer = tnt.BLINKTIMER - minetest.sound_play("tnt_ignite", {pos = self.object:get_pos(), gain = 1.0, max_hear_distance = 15}, true) -end -register_minecart({ - itemstring = "mcl_minecarts:tnt_minecart", - craft = { - output = "mcl_minecarts:tnt_minecart", - recipe = { - {"mcl_tnt:tnt"}, - {"mcl_minecarts:minecart"}, - }, - }, - entity_id = "mcl_minecarts:tnt_minecart", - description = S("Minecart with TNT"), - tt_help = S("Vehicle for fast travel on rails").."\n"..S("Can be ignited by tools or powered activator rail"), - longdesc = S("A minecart with TNT is an explosive vehicle that travels on rail."), - usagehelp = S("Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.") .. "\n" .. - S("To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited."), - initial_properties = { - mesh = "mcl_minecarts_minecart_block.b3d", - textures = { - "default_tnt_top.png", - "default_tnt_bottom.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "mcl_minecarts_minecart.png", - }, - }, - icon = "mcl_minecarts_minecart_tnt.png", - drop = {"mcl_minecarts:minecart", "mcl_tnt:tnt"}, - on_rightclick = function(self, clicker) - -- Ingite - if not clicker or not clicker:is_player() then - return - end - if self._boomtimer then - return - end - local held = clicker:get_wielded_item() - if held:get_name() == "mcl_fire:flint_and_steel" then - if not minetest.is_creative_enabled(clicker:get_player_name()) then - held:add_wear(65535/65) -- 65 uses - local index = clicker:get_wield_index() - local inv = clicker:get_inventory() - inv:set_stack("main", index, held) - end - activate_tnt_minecart(self) - end - end, - on_activate_by_rail = activate_tnt_minecart, - creative = true, - _mcl_minecarts_on_step = function(self, dtime) - -- Update TNT stuff - if self._boomtimer then - -- Explode - self._boomtimer = self._boomtimer - dtime - local pos = self.object:get_pos() - if self._boomtimer <= 0 then - self.object:remove() - mcl_explosions.explode(pos, 6, { drop_chance = 1.0 }) - return - else - tnt.smoke_step(pos) - end - end - if self._blinktimer then - self._blinktimer = self._blinktimer - dtime - if self._blinktimer <= 0 then - self._blink = not self._blink - if self._blink then - self.object:set_properties({textures = - { - "default_tnt_top.png", - "default_tnt_bottom.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "default_tnt_side.png", - "mcl_minecarts_minecart.png", - }}) - else - self.object:set_properties({textures = - { - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_minecarts_minecart.png", - }}) - end - self._blinktimer = tnt.BLINKTIMER - end - end - end, -}) +dofile(modpath.."/carts/minecart.lua") +dofile(modpath.."/carts/with_chest.lua") +dofile(modpath.."/carts/with_commandblock.lua") +dofile(modpath.."/carts/with_hopper.lua") +dofile(modpath.."/carts/with_furnace.lua") +dofile(modpath.."/carts/with_tnt.lua") if minetest.get_modpath("mcl_wip") then mcl_wip.register_wip_item("mcl_minecarts:chest_minecart") diff --git a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua new file mode 100644 index 000000000..a28b0a8d3 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua @@ -0,0 +1,84 @@ +local modname = minetest.get_current_modname() +local S = minetest.get_translator(modname) + +local function activate_normal_minecart(self) + detach_driver(self) + + -- Detach passenger + if self._passenger then + local mob = self._passenger.object + mob:set_detach() + end +end + +mcl_minecarts.register_minecart({ + itemstring = "mcl_minecarts:minecart", + craft = { + output = "mcl_minecarts:minecart", + recipe = { + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot"}, + }, + }, + entity_id = "mcl_minecarts:minecart", + description = S("Minecart"), + tt_helop = S("Vehicle for fast travel on rails"), + long_descp = S("Minecarts can be used for a quick transportion on rails.") .. "\n" .. + S("Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type."), + S("You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.") .. "\n" .. + S("To obtain the minecart, punch it while holding down the sneak key.") .. "\n" .. + S("If it moves over a powered activator rail, you'll get ejected."), + initial_properties = { + mesh = "mcl_minecarts_minecart.b3d", + textures = {"mcl_minecarts_minecart.png"}, + }, + icon = "mcl_minecarts_minecart_normal.png", + drop = {"mcl_minecarts:minecart"}, + on_rightclick = function(self, clicker) + local name = clicker:get_player_name() + if not clicker or not clicker:is_player() then + return + end + local player_name = clicker:get_player_name() + if self._driver and player_name == self._driver then + --detach_driver(self) + elseif not self._driver and not clicker:get_player_control().sneak then + self._driver = player_name + self._start_pos = self.object:get_pos() + mcl_player.player_attached[player_name] = true + clicker:set_attach(self.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) + mcl_player.player_attached[name] = true + minetest.after(0.2, function(name) + local player = minetest.get_player_by_name(name) + if player then + mcl_player.player_set_animation(player, "sit" , 30) + player:set_eye_offset(vector.new(0,-5.5,0), vector.new(0,-4,0)) + mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) + end + end, name) + end + end, + on_activate_by_rail = activate_normal_minecart, + _mcl_minecarts_on_step = function(self, dtime) + -- Grab mob + if math.random(1,20) > 15 and not self._passenger then + local mobsnear = minetest.get_objects_inside_radius(self.object:get_pos(), 1.3) + for n=1, #mobsnear do + local mob = mobsnear[n] + if mob then + local entity = mob:get_luaentity() + if entity and entity.is_mob then + self._passenger = entity + mob:set_attach(self.object, "", PASSENGER_ATTACH_POSITION, vector.zero()) + break + end + end + end + elseif self._passenger then + local passenger_pos = self._passenger.object:get_pos() + if not passenger_pos then + self._passenger = nil + end + end + end +}) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_chest.lua b/mods/ENTITIES/mcl_minecarts/carts/with_chest.lua new file mode 100644 index 000000000..1f141eade --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/carts/with_chest.lua @@ -0,0 +1,35 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +-- Minecart with Chest +mcl_minecarts.register_minecart({ + itemstring = "mcl_minecarts:chest_minecart", + craft = { + output = "mcl_minecarts:chest_minecart", + recipe = { + {"mcl_chests:chest"}, + {"mcl_minecarts:minecart"}, + }, + }, + entity_id = "mcl_minecarts:chest_minecart", + description = S("Minecart with Chest"), + tt_help = nil, + longdesc = nil, + usagehelp = nil, + initial_properties = { + mesh = "mcl_minecarts_minecart_chest.b3d", + textures = { + "mcl_chests_normal.png", + "mcl_minecarts_minecart.png" + }, + }, + icon = "mcl_minecarts_minecart_chest.png", + drop = {"mcl_minecarts:minecart", "mcl_chests:chest"}, + groups = { container = 1 }, + on_rightclick = nil, + on_activate_by_rail = nil, + creative = true +}) +mcl_entity_invs.register_inv("mcl_minecarts:chest_minecart","Minecart",27,false,true) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_commandblock.lua b/mods/ENTITIES/mcl_minecarts/carts/with_commandblock.lua new file mode 100644 index 000000000..0dc05d490 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/carts/with_commandblock.lua @@ -0,0 +1,64 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +function table_metadata(table) + return { + table = table, + set_string = function(self, key, value) + --print("set_string("..tostring(key)..", "..tostring(value)..")") + self.table[key] = tostring(value) + end, + get_string = function(self, key) + if self.table[key] then + return tostring(self.table[key]) + end + end + } +end + +-- Minecart with Command Block +mod.register_minecart({ + itemstring = "mcl_minecarts:command_block_minecart", + entity_id = "mcl_minecarts:command_block_minecart", + description = S("Minecart with Command Block"), + tt_help = nil, + loncdesc = nil, + usagehelp = nil, + initial_properties = { + mesh = "mcl_minecarts_minecart_block.b3d", + textures = { + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "jeija_commandblock_off.png^[verticalframe:2:0", + "mcl_minecarts_minecart.png", + }, + }, + icon = "mcl_minecarts_minecart_command_block.png", + drop = {"mcl_minecarts:minecart"}, + on_rightclick = function(self, clicker) + self._staticdata.meta = self._staticdata.meta or {} + local meta = table_metadata(self._staticdata.meta) + + mesecon.commandblock.handle_rightclick(meta, clicker) + end, + _mcl_minecarts_on_place = function(self, placer) + -- Create a fake metadata object that stores into the cart's staticdata + self._staticdata.meta = self._staticdata.meta or {} + local meta = table_metadata(self._staticdata.meta) + + mesecon.commandblock.initialize(meta) + mesecon.commandblock.place(meta, placer) + end, + on_activate_by_rail = function(self, timer) + self._staticdata.meta = self._staticdata.meta or {} + local meta = table_metadata(self._staticdata.meta) + + mesecon.commandblock.action_on(meta, self.object:get_pos()) + end, + creative = true +}) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua b/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua new file mode 100644 index 000000000..f2c6355b6 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua @@ -0,0 +1,92 @@ +local modname = minetest.get_current_modname() +local S = minetest.get_translator(modname) + +-- Minecart with Furnace +mcl_minecarts.register_minecart({ + itemstring = "mcl_minecarts:furnace_minecart", + craft = { + output = "mcl_minecarts:furnace_minecart", + recipe = { + {"mcl_furnaces:furnace"}, + {"mcl_minecarts:minecart"}, + }, + }, + entity_id = "mcl_minecarts:furnace_minecart", + description = S("Minecart with Furnace"), + tt_help = nil, + longdesc = S("A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel."), + usagehelp = S("Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.") .. "\n" .. + S("To obtain the minecart and furnace, punch them while holding down the sneak key."), + + initial_properties = { + mesh = "mcl_minecarts_minecart_block.b3d", + textures = { + "default_furnace_top.png", + "default_furnace_top.png", + "default_furnace_front.png", + "default_furnace_side.png", + "default_furnace_side.png", + "default_furnace_side.png", + "mcl_minecarts_minecart.png", + }, + }, + icon = "mcl_minecarts_minecart_furnace.png", + drop = {"mcl_minecarts:minecart", "mcl_furnaces:furnace"}, + on_rightclick = function(self, clicker) + local staticdata = self._staticdata + + -- Feed furnace with coal + if not clicker or not clicker:is_player() then + return + end + local held = clicker:get_wielded_item() + if minetest.get_item_group(held:get_name(), "coal") == 1 then + staticdata.fueltime = (staticdata.fueltime or 0) + 180 + + -- Trucate to 27 minutes (9 uses) + if staticdata.fueltime > 27*60 then + staticdata.fuel_time = 27*60 + end + + if not minetest.is_creative_enabled(clicker:get_player_name()) then + held:take_item() + local index = clicker:get_wield_index() + local inv = clicker:get_inventory() + inv:set_stack("main", index, held) + end + self.object:set_properties({textures = + { + "default_furnace_top.png", + "default_furnace_top.png", + "default_furnace_front_active.png", + "default_furnace_side.png", + "default_furnace_side.png", + "default_furnace_side.png", + "mcl_minecarts_minecart.png", + }}) + end + end, + on_activate_by_rail = nil, + creative = true, + _mcl_minecarts_on_step = function(self, dtime) + local staticdata = self._staticdata + + -- Update furnace stuff + if (staticdata.fueltime or 0) > 0 then + staticdata.fueltime = (staticdata.fueltime or dtime) - dtime + if staticdata.fueltime <= 0 then + self.object:set_properties({textures = + { + "default_furnace_top.png", + "default_furnace_top.png", + "default_furnace_front.png", + "default_furnace_side.png", + "default_furnace_side.png", + "default_furnace_side.png", + "mcl_minecarts_minecart.png", + }}) + staticdata.fueltime = 0 + end + end + end, +}) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua b/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua new file mode 100644 index 000000000..cb16b4cc6 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua @@ -0,0 +1,178 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +local LOGGING_ON = minetest.settings:get_bool("mcl_logging_minecarts", false) +local function mcl_log(message) + if LOGGING_ON then + mcl_util.mcl_log(message, "[Minecarts]", true) + end +end + +local function hopper_take_item(self, dtime) + local pos = self.object:get_pos() + if not pos then return end + + if not self or self.name ~= "mcl_minecarts:hopper_minecart" then return end + + if mcl_util.check_dtime_timer(self, dtime, "hoppermc_take", 0.15) then + --minetest.log("The check timer was triggered: " .. dump(pos) .. ", name:" .. self.name) + else + --minetest.log("The check timer was not triggered") + return + end + + + local above_pos = vector.offset(pos, 0, 0.9, 0) + local objs = minetest.get_objects_inside_radius(above_pos, 1.25) + + if objs then + mcl_log("there is an itemstring. Number of objs: ".. #objs) + + for k, v in pairs(objs) do + local ent = v:get_luaentity() + + if ent and not ent._removed and ent.itemstring and ent.itemstring ~= "" then + local taken_items = false + + mcl_log("ent.name: " .. tostring(ent.name)) + mcl_log("ent pos: " .. tostring(ent.object:get_pos())) + + local inv = mcl_entity_invs.load_inv(self, 5) + if not inv then return false end + + local current_itemstack = ItemStack(ent.itemstring) + + mcl_log("inv. size: " .. self._inv_size) + if inv:room_for_item("main", current_itemstack) then + mcl_log("Room") + inv:add_item("main", current_itemstack) + ent.object:get_luaentity().itemstring = "" + ent.object:remove() + taken_items = true + else + mcl_log("no Room") + end + + if not taken_items then + local items_remaining = current_itemstack:get_count() + + -- This will take part of a floating item stack if no slot can hold the full amount + for i = 1, self._inv_size, 1 do + local stack = inv:get_stack("main", i) + + mcl_log("i: " .. tostring(i)) + mcl_log("Items remaining: " .. items_remaining) + mcl_log("Name: " .. tostring(stack:get_name())) + + if current_itemstack:get_name() == stack:get_name() then + mcl_log("We have a match. Name: " .. tostring(stack:get_name())) + + local room_for = stack:get_stack_max() - stack:get_count() + mcl_log("Room for: " .. tostring(room_for)) + + if room_for == 0 then + -- Do nothing + mcl_log("No room") + elseif room_for < items_remaining then + mcl_log("We have more items remaining than space") + + items_remaining = items_remaining - room_for + stack:set_count(stack:get_stack_max()) + inv:set_stack("main", i, stack) + taken_items = true + else + local new_stack_size = stack:get_count() + items_remaining + stack:set_count(new_stack_size) + mcl_log("We have more than enough space. Now holds: " .. new_stack_size) + + inv:set_stack("main", i, stack) + items_remaining = 0 + + ent.object:get_luaentity().itemstring = "" + ent.object:remove() + + taken_items = true + break + end + + mcl_log("Count: " .. tostring(stack:get_count())) + mcl_log("stack max: " .. tostring(stack:get_stack_max())) + --mcl_log("Is it empty: " .. stack:to_string()) + end + + if i == self._inv_size and taken_items then + mcl_log("We are on last item and still have items left. Set final stack size: " .. items_remaining) + current_itemstack:set_count(items_remaining) + --mcl_log("Itemstack2: " .. current_itemstack:to_string()) + ent.itemstring = current_itemstack:to_string() + end + end + end + + --Add in, and delete + if taken_items then + mcl_log("Saving") + mcl_entity_invs.save_inv(ent) + return taken_items + else + mcl_log("No need to save") + end + + end + end + end + + return false +end + +-- Minecart with Hopper +mod.register_minecart({ + itemstring = "mcl_minecarts:hopper_minecart", + craft = { + output = "mcl_minecarts:hopper_minecart", + recipe = { + {"mcl_hoppers:hopper"}, + {"mcl_minecarts:minecart"}, + }, + }, + entity_id = "mcl_minecarts:hopper_minecart", + description = S("Minecart with Hopper"), + tt_help = nil, + longdesc = nil, + usagehelp = nil, + initial_properties = { + mesh = "mcl_minecarts_minecart_hopper.b3d", + textures = { + "mcl_hoppers_hopper_inside.png", + "mcl_minecarts_minecart.png", + "mcl_hoppers_hopper_outside.png", + "mcl_hoppers_hopper_top.png", + }, + }, + icon = "mcl_minecarts_minecart_hopper.png", + drop = {"mcl_minecarts:minecart", "mcl_hoppers:hopper"}, + groups = { container = 1 }, + on_rightclick = nil, + on_activate_by_rail = nil, + _mcl_minecarts_on_enter = function(self, pos) + local staticdata = self._staticdata + if (staticdata.hopper_delay or 0) > 0 then + return + end + + -- try to pull from containers into our inventory + local inv = mcl_entity_invs.load_inv(self,5) + local above_pos = pos + vector.new(0,1,0) + mcl_util.hopper_pull_to_inventory(inv, 'main', above_pos, pos) + + staticdata.hopper_delay = (staticdata.hopper_delay or 0) + (1/20) + end, + _mcl_minecarts_on_step = function(self, dtime) + hopper_take_item(self, dtime) + end, + creative = true +}) +mcl_entity_invs.register_inv("mcl_minecarts:hopper_minecart", "Hopper Minecart", 5, false, true) + diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua b/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua new file mode 100644 index 000000000..fcbe03f8d --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua @@ -0,0 +1,123 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +-- Minecart with TNT +local function activate_tnt_minecart(self, timer) + if self._boomtimer then + return + end + if timer then + self._boomtimer = timer + else + self._boomtimer = tnt.BOOMTIMER + end + self.object:set_properties({textures = { + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_minecarts_minecart.png", + }}) + self._blinktimer = tnt.BLINKTIMER + minetest.sound_play("tnt_ignite", {pos = self.object:get_pos(), gain = 1.0, max_hear_distance = 15}, true) +end +mod.register_minecart({ + itemstring = "mcl_minecarts:tnt_minecart", + craft = { + output = "mcl_minecarts:tnt_minecart", + recipe = { + {"mcl_tnt:tnt"}, + {"mcl_minecarts:minecart"}, + }, + }, + entity_id = "mcl_minecarts:tnt_minecart", + description = S("Minecart with TNT"), + tt_help = S("Vehicle for fast travel on rails").."\n"..S("Can be ignited by tools or powered activator rail"), + longdesc = S("A minecart with TNT is an explosive vehicle that travels on rail."), + usagehelp = S("Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.") .. "\n" .. + S("To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited."), + initial_properties = { + mesh = "mcl_minecarts_minecart_block.b3d", + textures = { + "default_tnt_top.png", + "default_tnt_bottom.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "mcl_minecarts_minecart.png", + }, + }, + icon = "mcl_minecarts_minecart_tnt.png", + drop = {"mcl_minecarts:minecart", "mcl_tnt:tnt"}, + on_rightclick = function(self, clicker) + -- Ingite + if not clicker or not clicker:is_player() then + return + end + if self._boomtimer then + return + end + local held = clicker:get_wielded_item() + if held:get_name() == "mcl_fire:flint_and_steel" then + if not minetest.is_creative_enabled(clicker:get_player_name()) then + held:add_wear(65535/65) -- 65 uses + local index = clicker:get_wield_index() + local inv = clicker:get_inventory() + inv:set_stack("main", index, held) + end + activate_tnt_minecart(self) + end + end, + on_activate_by_rail = activate_tnt_minecart, + creative = true, + _mcl_minecarts_on_step = function(self, dtime) + -- Update TNT stuff + if self._boomtimer then + -- Explode + self._boomtimer = self._boomtimer - dtime + local pos = self.object:get_pos() + if self._boomtimer <= 0 then + self.object:remove() + mcl_explosions.explode(pos, 6, { drop_chance = 1.0 }) + return + else + tnt.smoke_step(pos) + end + end + if self._blinktimer then + self._blinktimer = self._blinktimer - dtime + if self._blinktimer <= 0 then + self._blink = not self._blink + if self._blink then + self.object:set_properties({textures = + { + "default_tnt_top.png", + "default_tnt_bottom.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "default_tnt_side.png", + "mcl_minecarts_minecart.png", + }}) + else + self.object:set_properties({textures = + { + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_minecarts_minecart.png", + }}) + end + self._blinktimer = tnt.BLINKTIMER + end + end + end, +}) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 0b0d664ce..6b8d067bc 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -312,3 +312,48 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) return dir end +function mcl_minecarts:update_cart_orientation() + local staticdata = self._staticdata + + -- constants + local _2_pi = math.pi * 2 + local pi = math.pi + local dir = staticdata.dir + + -- Calculate an angle from the x,z direction components + local rot_y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) + if rot_y < 0 then + rot_y = rot_y + _2_pi + end + + -- Check if the rotation is a 180 flip and don't change if so + local rot = self.object:get_rotation() + local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) + if diff < 0.001 or diff > _2_pi - 0.001 then + -- Update rotation adjust and recalculate the rotation + staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi + rot.y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) + else + rot.y = rot_y + end + + -- Forward/backwards tilt (pitch) + if dir.y < 0 then + rot.x = -0.25 * pi + elseif dir.y > 0 then + rot.x = 0.25 * pi + else + rot.x = 0 + end + + if ( staticdata.rot_adjust or 0 ) < 0.01 then + rot.x = -rot.x + end + if dir.z ~= 0 then + rot.x = -rot.x + end + + self.object:set_rotation(rot) +end + + diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index d0fbb0b64..8f8b6b4ef 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -1,11 +1,14 @@ local modname = minetest.get_current_modname() - +local modpath = minetest.get_modpath(modname) mcl_minecarts = {} local mod = mcl_minecarts -mcl_minecarts.modpath = minetest.get_modpath(modname) +mcl_minecarts.modpath = modpath + +-- Constants mcl_minecarts.speed_max = 10 mcl_minecarts.check_float_time = 15 +mcl_minecarts.FRICTION = 0.4 -dofile(mcl_minecarts.modpath.."/functions.lua") -dofile(mcl_minecarts.modpath.."/rails.lua") -dofile(mcl_minecarts.modpath.."/carts.lua") +for _,filename in pairs({"functions","rails","train","storage","carts"}) do + dofile(modpath.."/"..filename..".lua") +end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua new file mode 100644 index 000000000..da5f2b791 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -0,0 +1,496 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +-- Constants +local DEBUG = false +local friction = mcl_minecarts.FRICTION + +-- Imports +local train_length = mod.train_length +local update_train = mod.update_train +local update_cart_orientation = mod.update_cart_orientation +local get_cart_data = mod.get_cart_data + +local function detach_minecart(self) + local staticdata = self._staticdata + + staticdata.connected_at = nil + self.object:set_velocity(staticdata.dir * staticdata.velocity) +end +mod.detach_minecart = detach_minecart + +local function try_detach_minecart(self) + local staticdata = self._staticdata + + local node = minetest.get_node(staticdata.connected_at) + if minetest.get_item_group(node.name, "rail") == 0 then + detach_minecart(self) + end +end + +--[[ + Array of hooks { {u,v,w}, name } + Actual position is pos + u * dir + v * right + w * up +]] +local enter_exit_checks = { + { 0, 0, 0, "" }, + { 0, 0, 1, "_above" }, + { 0, 0,-1, "_below" }, + { 0, 1, 0, "_side" }, + { 0,-1, 0, "_side" }, +} + +local function handle_cart_enter_exit(self, pos, next_dir, event) + local staticdata = self._staticdata + + local dir = staticdata.dir + local right = vector.new( dir.z, dir.y, -dir.x) + local up = vector.new(0,1,0) + for _,check in ipairs(enter_exit_checks) do + local check_pos = pos + dir * check[1] + right * check[2] + up * check[3] + local node = minetest.get_node(check_pos) + local node_def = minetest.registered_nodes[node.name] + if node_def then + -- node-specific hook + local hook_name = "_mcl_minecarts_"..event..check[4] + local hook = node_def[hook_name] + if hook then hook(check_pos, self, next_dir, pos) end + + -- global minecart hook + hook = mcl_minecarts[event..check[4]] + if hook then hook(check_pos, self, next_dir, node_def) end + end + end + + -- Handle cart-specific behaviors + local hook = self["_mcl_minecarts_"..event] + if hook then hook(self, pos) end +end +local function set_metadata_cart_status(pos, uuid, state) + local meta = minetest.get_meta(pos) + local carts = minetest.deserialize(meta:get_string("_mcl_minecarts_carts")) or {} + carts[uuid] = state + meta:set_string("_mcl_minecarts_carts", minetest.serialize(carts)) +end +local function handle_cart_enter(self, pos, next_dir) + --print("entering "..tostring(pos)) + set_metadata_cart_status(pos, self._staticdata.uuid, 1) + handle_cart_enter_exit(self, pos, next_dir, "on_enter" ) +end +local function handle_cart_leave(self, pos, next_dir) + --print("leaving "..tostring(pos)) + set_metadata_cart_status(pos, self._staticdata.uuid, nil) + handle_cart_enter_exit(self, pos, next_dir, "on_leave" ) +end +local function handle_cart_node_watches(self, dtime) + local staticdata = self._staticdata + local watches = staticdata.node_watches or {} + local new_watches = {} + for _,node_pos in ipairs(watches) do + local node = minetest.get_node(node_pos) + local node_def = minetest.registered_nodes[node.name] + if node_def then + local hook = node_def._mcl_minecarts_node_on_step + if hook and hook(node_pos, self, dtime) then + new_watches[#new_watches+1] = node_pos + end + end + end + + staticdata.node_watches = new_watches +end + +local function handle_cart_collision(cart1, prev_pos, next_dir) + -- Look ahead one block + local pos = vector.add(prev_pos, next_dir) + + local meta = minetest.get_meta(pos) + local carts = minetest.deserialize(meta:get_string("_mcl_minecarts_carts")) or {} + local cart_uuid = nil + local dirty = false + for uuid,v in pairs(carts) do + -- Clean up dead carts + if not get_cart_data(uuid) then + carts[uuid] = nil + dirty = true + uuid = nil + end + + if uuid and uuid ~= cart1._staticdata.uuid then cart_uuid = uuid end + end + if dirty then + meta:set_string("_mcl_minecarts_carts",minetest.serialize(carts)) + end + + local meta = minetest.get_meta(vector.add(pos,next_dir)) + if not cart_uuid then return end + minetest.log("action","cart #"..cart1._staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos)) + + local cart2_aoid = mcl_util.get_active_object_id_from_uuid(cart_uuid) + local cart2 = minetest.luaentities[cart2_aoid] + if not cart2 then return end + + -- Standard Collision Handling + local cart1_staticdata = cart1._staticdata + local cart2_staticdata = cart2._staticdata + + local u1 = cart1_staticdata.velocity + local u2 = cart2_staticdata.velocity + local m1 = cart1_staticdata.mass + local m2 = cart2_staticdata.mass + + if u2 == 0 and u1 < 1 and train_length(cart1) < 3 then + -- Link carts + cart2_staticdata.behind = cart1_staticdata.uuid + cart1_staticdata.ahead = cart1_staticdata.uuid + cart2_staticdata.velocity = cart1_staticdata.velocity + return + end + + -- Calculate new velocities according to https://en.wikipedia.org/wiki/Elastic_collision#One-dimensional_Newtonian + local c1 = m1 + m2 + local d = m1 - m2 + local v1 = ( d * u1 + 2 * m2 * u2 ) / c1 + local v2 = ( 2 * m1 * u1 + d * u2 ) / c1 + + cart1_staticdata.velocity = v1 + cart2_staticdata.velocity = v2 + + -- Force the other cart to move the same direction this one was + cart2_staticdata.dir = mcl_minecarts:get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) +end + + +local function vector_away_from_players(self, staticdata) + local objs = minetest.get_objects_inside_radius(self.object:get_pos(), 1.1) + for n=1,#objs do + local obj = objs[n] + local player_name = obj:get_player_name() + if player_name and player_name ~= "" and not ( self._driver and self._driver == player_name ) then + return obj:get_pos() - self.object:get_pos() + end + end + + return nil +end + +local function direction_away_from_players(self, staticdata) + local diff = vector_away_from_players(self, staticdata) + if not diff then return 0 end + + local length = vector.distance(vector.new(0,0,0),diff) + local vec = diff / length + local force = vector.dot( vec, vector.normalize(staticdata.dir) ) + + -- Check if this would push past the end of the track and don't move it it would + -- This prevents an oscillation that would otherwise occur + local dir = staticdata.dir + if force > 0 then + dir = -dir + end + if mcl_minecarts:is_rail( staticdata.connected_at + dir ) then + if force > 0.5 then + return -length * 4 + elseif force < -0.5 then + return length * 4 + end + end + return 0 +end + +local function calculate_acceleration(self, staticdata) + local acceleration = 0 + + -- Fix up movement data + staticdata.velocity = staticdata.velocity or 0 + + -- Apply friction if moving + if staticdata.velocity > 0 then + acceleration = -friction + end + + local pos = staticdata.connected_at + local node_name = minetest.get_node(pos).name + local node_def = minetest.registered_nodes[node_name] + local max_vel = mcl_minecarts.speed_max + + if self._go_forward then + acceleration = 4 + elseif self._brake then + acceleration = -1.5 + elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then + acceleration = 0.6 + elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then + -- Standard friction + elseif node_def and node_def._rail_acceleration then + acceleration = node_def._rail_acceleration * 4 + end + + -- Factor in gravity after everything else + local gravity_strength = 2.45 --friction * 5 + if staticdata.dir.y < 0 then + acceleration = gravity_strength - friction + elseif staticdata.dir.y > 0 then + acceleration = -gravity_strength + friction + end + + return acceleration +end + +local function reverse_direction(self, staticdata) + -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at + local next_dir = -staticdata.dir + staticdata.connected_at = staticdata.connected_at + staticdata.dir + staticdata.distance = 1 - staticdata.distance + + -- recalculate direction + local next_dir,_ = mcl_minecarts:get_rail_direction(staticdata.connected_at, next_dir, nil, nil, staticdata.railtype) + staticdata.dir = next_dir +end + +local function do_movement_step(self, dtime) + local staticdata = self._staticdata + if not staticdata.connected_at then return 0 end + + -- Calculate timestep remaiing in this block + local x_0 = staticdata.distance or 0 + local remaining_in_block = 1 - x_0 + local a = calculate_acceleration(self, staticdata) + local v_0 = staticdata.velocity + + -- Repel minecarts + local away = direction_away_from_players(self, staticdata) + if away > 0 then + v_0 = away + elseif away < 0 then + reverse_direction(self, staticdata) + v_0 = -away + end + + if DEBUG and ( v_0 > 0 or a ~= 0 ) then + print( " cart "..tostring(staticdata.uuid).. + ": a="..tostring(a).. + ",v_0="..tostring(v_0).. + ",x_0="..tostring(x_0).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir).. + ",connected_at="..tostring(staticdata.connected_at).. + ",distance="..tostring(staticdata.distance) + ) + end + + -- Not moving + if a == 0 and v_0 == 0 then return 0 end + + -- Movement equation with acceleration: x_1 = x_0 + v_0 * t + 0.5 * a * t*t + local timestep + local stops_in_block = false + local inside = v_0 * v_0 + 2 * a * remaining_in_block + if inside < 0 then + -- Would stop or reverse direction inside this block, calculate time to v_1 = 0 + timestep = -v_0 / a + stops_in_block = true + elseif a ~= 0 then + -- Setting x_1 = x_0 + remaining_in_block, and solving for t gives: + timestep = ( math.sqrt( v_0 * v_0 + 2 * a * remaining_in_block) - v_0 ) / a + else + timestep = remaining_in_block / v_0 + end + + -- Truncate timestep to remaining time delta + if timestep > dtime then + timestep = dtime + end + + -- Truncate timestep to prevent v_1 from being larger that speed_max + local v_max = mcl_minecarts.speed_max + if (v_0 ~= v_max) and ( v_0 + a * timestep > v_max) then + timestep = ( v_max - v_0 ) / a + end + + -- Prevent infinite loops + if timestep <= 0 then return 0 end + + -- Calculate v_1 taking v_max into account + local v_1 = v_0 + a * timestep + if v_1 > v_max then + v_1 = v_max + elseif v_1 < friction / 5 then + v_1 = 0 + end + + -- Calculate x_1 + local x_1 = x_0 + timestep * v_0 + 0.5 * a * timestep * timestep + + -- Update position and velocity of the minecart + staticdata.velocity = v_1 + staticdata.distance = x_1 + + if DEBUG and (1==0) and ( v_0 > 0 or a ~= 0 ) then + print( "- cart #"..tostring(staticdata.uuid).. + ": a="..tostring(a).. + ",v_0="..tostring(v_0).. + ",v_1="..tostring(v_1).. + ",x_0="..tostring(x_0).. + ",x_1="..tostring(x_1).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir).. + ",connected_at="..tostring(staticdata.connected_at).. + ",distance="..tostring(staticdata.distance) + ) + end + + -- Entity movement + local pos = staticdata.connected_at + + -- Handle movement to next block, account for loss of precision in calculations + if x_1 >= 0.99 then + staticdata.distance = 0 + + -- Anchor at the next node + local old_pos = pos + pos = pos + staticdata.dir + staticdata.connected_at = pos + + -- Get the next direction + local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) + if DEBUG and next_dir ~= staticdata.dir then + print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) + end + + -- Handle cart collisions + handle_cart_collision(self, pos, next_dir) + + -- Leave the old node + handle_cart_leave(self, old_pos, next_dir ) + + -- Enter the new node + handle_cart_enter(self, pos, next_dir) + + try_detach_minecart(self) + + -- Handle end of track + if next_dir == staticdata.dir * -1 and next_dir.y == 0 then + if DEBUG then print("Stopping cart at end of track at "..tostring(pos)) end + staticdata.velocity = 0 + end + + -- Update cart direction + staticdata.dir = next_dir + elseif stops_in_block and v_1 < (friction/5) and a <= 0 then + -- Handle direction flip due to gravity + if DEBUG then print("Gravity flipped direction") end + + -- Velocity should be zero at this point + staticdata.velocity = 0 + + reverse_direction(self, staticdata) + + -- Intermediate movement + pos = staticdata.connected_at + staticdata.dir * staticdata.distance + else + -- Intermediate movement + pos = pos + staticdata.dir * staticdata.distance + end + + self.object:move_to(pos) + + -- Update cart orientation + update_cart_orientation(self) + + -- Debug reporting + if DEBUG and ( v_0 > 0 or v_1 > 0 ) then + print( " cart #"..tostring(staticdata.uuid).. + ": a="..tostring(a).. + ",v_0="..tostring(v_0).. + ",v_1="..tostring(v_1).. + ",x_0="..tostring(x_0).. + ",x_1="..tostring(x_1).. + ",timestep="..tostring(timestep).. + ",dir="..tostring(staticdata.dir).. + ",pos="..tostring(pos).. + ",connected_at="..tostring(staticdata.connected_at).. + ",distance="..tostring(staticdata.distance) + ) + end + + -- Report the amount of time processed + return dtime - timestep +end + +local function do_movement( self, dtime ) + local staticdata = self._staticdata + + -- Allow the carts to be delay for the rest of the world to react before moving again + if ( staticdata.delay or 0 ) > dtime then + staticdata.delay = staticdata.delay - dtime + return + else + staticdata.delay = 0 + end + + -- Break long movements at block boundaries to make it + -- it impossible to jump across gaps due to server lag + -- causing large timesteps + while dtime > 0 do + local new_dtime = do_movement_step(self, dtime) + + update_train(self) + + -- Handle node watches here in steps to prevent server lag from changing behavior + handle_cart_node_watches(self, dtime - new_dtime) + + dtime = new_dtime + end +end + +local function do_detached_movement(self, dtime) + local staticdata = self._staticdata + + -- Make sure the object is still valid before trying to move it + if not self.object or not self.object:get_pos() then return end + + -- Apply physics + if mcl_physics then + mcl_physics.apply_entity_environmental_physics(self) + else + -- Simple physics + local friction = self.object:get_velocity() or vector.new(0,0,0) + friction.y = 0 + + local accel = vector.new(0,-9.81,0) -- gravity + accel = vector.add(accel, vector.multiply(friction,-0.9)) + self.object:set_acceleration(accel) + end + + local away = vector_away_from_players(self, staticdata) + if away then + local v = self.object:get_velocity() + self.object:set_velocity(v - away) + end + + -- Try to reconnect to rail + local pos_r = vector.round(self.object:get_pos()) + local node = minetest.get_node(pos_r) + if minetest.get_item_group(node.name, "rail") ~= 0 then + staticdata.connected_at = pos_r + staticdata.railtype = node.name + + local freebody_velocity = self.object:get_velocity() + staticdata.dir = mod:get_rail_direction(pos_r, mod.snap_direction(freebody_velocity)) + + -- Use vector projection to only keep the velocity in the new direction of movement on the rail + -- https://en.wikipedia.org/wiki/Vector_projection + staticdata.velocity = vector.dot(staticdata.dir,freebody_velocity) + + -- Clear freebody movement + self.object:set_velocity(vector.new(0,0,0)) + self.object:set_acceleration(vector.new(0,0,0)) + end +end + +--return do_movement, do_detatched_movement +return do_movement,do_detached_movement,handle_cart_enter + diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index d0a2eaa4a..b3d304b9b 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -188,7 +188,7 @@ function mod.register_rail(itemstring, ndef) if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end - print("registering rail "..itemstring.." with definition: "..dump(ndef)) + --print("registering rail "..itemstring.." with definition: "..dump(ndef)) -- Make registrations minetest.register_node(itemstring, ndef) diff --git a/mods/ENTITIES/mcl_minecarts/storage.lua b/mods/ENTITIES/mcl_minecarts/storage.lua new file mode 100644 index 000000000..f4ee1af51 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/storage.lua @@ -0,0 +1,37 @@ +local storage = minetest.get_mod_storage() +local mod = mcl_minecarts + +local cart_data = {} +local cart_data_fail_cache = {} + +function mod.get_cart_data(uuid) + if cart_data[uuid] then return cart_data[uuid] end + if cart_data_fail_cache[uuid] then return nil end + + local data = minetest.deserialize(storage:get_string("cart-"..uuid)) + if not data then + cart_data_fail_cache[uuid] = true + return nil + end + + cart_data[uuid] = data + return data +end +local function save_cart_data(uuid) + if not cart_data[uuid] then return end + storage:set_string("cart-"..uuid,minetest.serialize(cart_data[uuid])) +end +mod.save_cart_data = save_cart_data + +function mod.update_cart_data(data) + local uuid = data.uuid + cart_data[uuid] = data + cart_data_fail_cache[uuid] = nil + save_cart_data(uuid) +end +function mod.destroy_cart_data(uuid) + storage:set_string("cart-"..uuid,"") + cart_data[uuid] = nil + cart_data_fail_cache[uuid] = true +end + diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua new file mode 100644 index 000000000..4e5163f01 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -0,0 +1,5 @@ +function mcl_minecarts.update_train(cart) +end + +function mcl_minecarts.train_length(cart) +end From eb0f82bdfbebbbedd5cecf1ce677375182e02ae8 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 6 Apr 2024 22:10:22 +0000 Subject: [PATCH 065/195] Fixish reorganizing, initial train implementation --- mods/ENTITIES/mcl_minecarts/carts.lua | 15 ----- mods/ENTITIES/mcl_minecarts/init.lua | 2 +- mods/ENTITIES/mcl_minecarts/movement.lua | 11 ++-- mods/ENTITIES/mcl_minecarts/train.lua | 71 +++++++++++++++++++++++- 4 files changed, 76 insertions(+), 23 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index a0af8d64c..d0b8296c9 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -27,21 +27,6 @@ local max_step_distance = 0.5 local MINECART_MAX_HP = 4 local PASSENGER_ATTACH_POSITION = vector.new(0, -1.75, 0) -local function detach_minecart(self) - local staticdata = self._staticdata - - staticdata.connected_at = nil - self.object:set_velocity(staticdata.dir * staticdata.velocity) -end -local function try_detach_minecart(self) - local staticdata = self._staticdata - - local node = minetest.get_node(staticdata.connected_at) - if minetest.get_item_group(node.name, "rail") == 0 then - detach_minecart(self) - end -end - local function detach_driver(self) if not self._driver then return diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 8f8b6b4ef..265428112 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -9,6 +9,6 @@ mcl_minecarts.speed_max = 10 mcl_minecarts.check_float_time = 15 mcl_minecarts.FRICTION = 0.4 -for _,filename in pairs({"functions","rails","train","storage","carts"}) do +for _,filename in pairs({"storage","functions","rails","train","carts"}) do dofile(modpath.."/"..filename..".lua") end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index da5f2b791..8881589d3 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -10,6 +10,7 @@ local friction = mcl_minecarts.FRICTION -- Imports local train_length = mod.train_length local update_train = mod.update_train +local link_cart_ahead = mod.link_cart_ahead local update_cart_orientation = mod.update_cart_orientation local get_cart_data = mod.get_cart_data @@ -141,10 +142,10 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) local m1 = cart1_staticdata.mass local m2 = cart2_staticdata.mass - if u2 == 0 and u1 < 1 and train_length(cart1) < 3 then - -- Link carts - cart2_staticdata.behind = cart1_staticdata.uuid - cart1_staticdata.ahead = cart1_staticdata.uuid + print("u1="..tostring(u1)..",u2="..tostring(u2)) + if u2 == 0 and u1 < 4 and train_length(cart1) < 3 then + link_cart_ahead(cart1, cart2) + cart2_staticdata.dir = mcl_minecarts:get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) cart2_staticdata.velocity = cart1_staticdata.velocity return end @@ -243,7 +244,7 @@ local function reverse_direction(self, staticdata) -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at local next_dir = -staticdata.dir staticdata.connected_at = staticdata.connected_at + staticdata.dir - staticdata.distance = 1 - staticdata.distance + staticdata.distance = 1 - (staticdata.distance or 0) -- recalculate direction local next_dir,_ = mcl_minecarts:get_rail_direction(staticdata.connected_at, next_dir, nil, nil, staticdata.railtype) diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index 4e5163f01..e9511a83d 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -1,5 +1,72 @@ -function mcl_minecarts.update_train(cart) +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts + +-- Imports +local get_cart_data = mod.get_cart_data + +local function find_back(start) + while start.behind do + local nxt = get_cart_data(start.behind) + if not nxt then return start end + start = nxt + end + return start end -function mcl_minecarts.train_length(cart) +local function train_cars(anchor) + local back = find_back(anchor._staticdata) + return function() + if not back then return end + + local ret = back + if back.ahead then + back = get_cart_data(back.ahead) + else + back = nil + end + return ret + end +end + +function mod.update_train(cart) + local sum_velocity = 0 + local count = 0 + for cart in train_cars(cart) do + count = count + 1 + sum_velocity = sum_velocity + (cart.velocity or 0) + end + local avg_velocity = sum_velocity / count + if count == 0 then return end + + print("Using velocity "..tostring(avg_velocity)) + + -- Set the entire train to the average velocity + for c in train_cars(cart) do + print(tostring(c.behind).."->"..c.uuid.."->"..tostring(c.ahead).." setting cart #"..c.uuid.." velocity to "..tostring(avg_velocity)) + c.velocity = avg_velocity + end +end + +function mod.train_length(cart) + local count = 0 + for cart in train_cars(cart) do + count = count + 1 + end + return count +end +function mod.link_cart_ahead(cart, cart_ahead) + local staticdata = cart._staticdata + local ca_staticdata = cart_ahead._staticdata + + minetest.log("action","Linking cart #"..staticdata.uuid.." to cart #"..ca_staticdata.uuid) + + staticdata.ahead = ca_staticdata.uuid + ca_staticdata.behind = staticdata.uuid +end +function mod.is_in_same_train(anchor, other) + for cart in train_cars(anchor) do + if cart.uuid == other.uuid then return true end + end + return false end From e680869cbdc7a4e03d13f8161edb04d975858c18 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 7 Apr 2024 07:13:40 +0000 Subject: [PATCH 066/195] Make sure carts get detatch if the rail under them is removed --- mods/ENTITIES/mcl_minecarts/movement.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 8881589d3..ba2ea827f 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -370,8 +370,6 @@ local function do_movement_step(self, dtime) -- Enter the new node handle_cart_enter(self, pos, next_dir) - try_detach_minecart(self) - -- Handle end of track if next_dir == staticdata.dir * -1 and next_dir.y == 0 then if DEBUG then print("Stopping cart at end of track at "..tostring(pos)) end @@ -437,6 +435,7 @@ local function do_movement( self, dtime ) -- causing large timesteps while dtime > 0 do local new_dtime = do_movement_step(self, dtime) + try_detach_minecart(self) update_train(self) From 40a518470331950a75a4d6c9ebbf17bc96cf142a Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 7 Apr 2024 07:44:29 +0000 Subject: [PATCH 067/195] Fix rail detach crash, make tnt minecarts explode if they hit something hard (off rails) --- .../ENTITIES/mcl_minecarts/carts/with_tnt.lua | 20 ++++++++++++++----- mods/ENTITIES/mcl_minecarts/movement.lua | 12 ++++++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua b/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua index fcbe03f8d..82bc1718e 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua @@ -3,7 +3,12 @@ local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts local S = minetest.get_translator(modname) --- Minecart with TNT +local function detonate_tnt_minecart(self) + local pos = self.object:get_pos() + self.object:remove() + mcl_explosions.explode(pos, 6, { drop_chance = 1.0 }) +end + local function activate_tnt_minecart(self, timer) if self._boomtimer then return @@ -76,19 +81,24 @@ mod.register_minecart({ on_activate_by_rail = activate_tnt_minecart, creative = true, _mcl_minecarts_on_step = function(self, dtime) - -- Update TNT stuff + -- Impacts reduce the speed greatly. Use this to trigger explosions + local current_speed = vector.length(self.object:get_velocity()) + if current_speed < (self._old_speed or 0) - 6 then + detonate_tnt_minecart(self) + end + self._old_speed = current_speed + if self._boomtimer then -- Explode self._boomtimer = self._boomtimer - dtime - local pos = self.object:get_pos() if self._boomtimer <= 0 then - self.object:remove() - mcl_explosions.explode(pos, 6, { drop_chance = 1.0 }) + detonate_tnt_minecart(self) return else tnt.smoke_step(pos) end end + if self._blinktimer then self._blinktimer = self._blinktimer - dtime if self._blinktimer <= 0 then diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index ba2ea827f..fd2edb422 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -24,6 +24,10 @@ mod.detach_minecart = detach_minecart local function try_detach_minecart(self) local staticdata = self._staticdata + if not staticdata then return end + + -- Don't try to detach if alread detached + if not staticdata.connected_at then return end local node = minetest.get_node(staticdata.connected_at) if minetest.get_item_group(node.name, "rail") == 0 then @@ -461,7 +465,13 @@ local function do_detached_movement(self, dtime) friction.y = 0 local accel = vector.new(0,-9.81,0) -- gravity - accel = vector.add(accel, vector.multiply(friction,-0.9)) + + -- Don't apply friction in the air + local pos_rounded = vector.round(self.object:get_pos()) + if minetest.get_node(vector.offset(pos_rounded,0,-1,0)).name ~= "air" then + accel = vector.add(accel, vector.multiply(friction,-0.9)) + end + self.object:set_acceleration(accel) end From 7bd95257bd002df802fa387ee08bcd27794e8ca4 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 7 Apr 2024 10:18:55 +0000 Subject: [PATCH 068/195] Give furnace minecart minimum velocity when lit, add train separation code, update logging code, add sequence number to entity staticdata to allow respawn/despawn when carts move when the entity is unloaded --- mods/CORE/mcl_util/init.lua | 12 +++ mods/ENTITIES/mcl_minecarts/carts.lua | 34 +++++-- .../mcl_minecarts/carts/with_furnace.lua | 4 + mods/ENTITIES/mcl_minecarts/init.lua | 7 +- mods/ENTITIES/mcl_minecarts/movement.lua | 23 +++-- mods/ENTITIES/mcl_minecarts/train.lua | 95 +++++++++++++------ 6 files changed, 127 insertions(+), 48 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 9df10ca7d..68478fc5a 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -76,6 +76,15 @@ function mcl_util.mcl_log(message, module, bypass_default_logger) minetest.log(selected_module .. " " .. message) end end +function mcl_util.make_mcl_logger(label, option) + -- Return dummy function if debug option isn't set + if not minetest.settings:get_bool(option,false) then return function() end, false end + + local label_text = "["..tostring(label).."]" + return function(message) + mcl_util.mcl_log(message, label_text, true) + end, true +end local player_timers = {} @@ -825,6 +834,9 @@ end function mcl_util.get_active_object_id_from_uuid(uuid) return uuid_to_aoid_cache[uuid] or scan_active_objects() or uuid_to_aoid_cache[uuid] end +function mcl_util.get_luaentity_from_uuid(uuid) + return minetest.luaentities[ mcl_util.get_active_object_id_from_uuid(uuid) ] +end function mcl_util.get_uuid(obj) local le = obj:get_luaentity() diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index d0b8296c9..f0e4fab65 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -3,13 +3,7 @@ local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts local S = minetest.get_translator(modname) -local LOGGING_ON = minetest.settings:get_bool("mcl_logging_minecarts", false) -local DEBUG = false -local function mcl_log(message) - if LOGGING_ON then - mcl_util.mcl_log(message, "[Minecarts]", true) - end -end +local mcl_log = mcl_util.make_mcl_logger("mcl_logging_minecarts", "Minecarts") -- Imports local table_merge = mcl_util.table_merge @@ -53,6 +47,7 @@ local function make_staticdata( railtype, connected_at, dir ) velocity = 0, dir = vector.new(dir), mass = 1, + seq = 1, } end @@ -86,8 +81,15 @@ function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) if not data.uuid then data.uuid = mcl_util.get_uuid(self.object) end + self._seq = data.seq or 1 + local cd = get_cart_data(data.uuid) - if not cd then update_cart_data(data) end + if not cd then + update_cart_data(data) + else + if not cd.seq then cd.seq = 1 end + data = cd + end -- Initialize if type(data) == "table" then @@ -121,7 +123,7 @@ function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) end function DEFAULT_CART_DEF:get_staticdata() save_cart_data(self._staticdata.uuid) - return minetest.serialize({uuid = self._staticdata.uuid}) + return minetest.serialize({uuid = self._staticdata.uuid, seq=self._seq}) end function DEFAULT_CART_DEF:add_node_watch(pos) @@ -147,6 +149,15 @@ function DEFAULT_CART_DEF:remove_node_watch(pos) end staticdata.node_watches = new_watches end +function DEFAULT_CART_DEF:get_cart_position() + local staticdata = self._staticdata + + if staticdata.connected_at then + return staticdata.connected_at + staticdata.dir * staticdata.distance + else + return self.object:get_pos() + end +end function DEFAULT_CART_DEF:on_step(dtime) local staticdata = self._staticdata if not staticdata then @@ -154,6 +165,11 @@ function DEFAULT_CART_DEF:on_step(dtime) self._staticdata = staticdata end + if self._seq ~= staticdata.seq then + print("TODO: remove cart #"..staticdata.uuid.." with sequence number mismatch") + print("self.seq="..tostring(self._seq)..", staticdata.seq="..tostring(staticdata.seq)) + end + -- Regen local hp = self.object:get_hp() if hp < MINECART_MAX_HP then diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua b/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua index f2c6355b6..13693b733 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua @@ -73,6 +73,10 @@ mcl_minecarts.register_minecart({ -- Update furnace stuff if (staticdata.fueltime or 0) > 0 then + if staticdata.velocity < 0.25 then + staticdata.velocity = 0.25 + end + staticdata.fueltime = (staticdata.fueltime or dtime) - dtime if staticdata.fueltime <= 0 then self.object:set_properties({textures = diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 265428112..22c67de5b 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -5,9 +5,10 @@ local mod = mcl_minecarts mcl_minecarts.modpath = modpath -- Constants -mcl_minecarts.speed_max = 10 -mcl_minecarts.check_float_time = 15 -mcl_minecarts.FRICTION = 0.4 +mod.speed_max = 10 +mod.check_float_time = 15 +mod.FRICTION = 0.4 +mod.MAX_TRAIN_LENGTH = 4 for _,filename in pairs({"storage","functions","rails","train","carts"}) do dofile(modpath.."/"..filename..".lua") diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index fd2edb422..25db9a621 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -4,8 +4,9 @@ local mod = mcl_minecarts local S = minetest.get_translator(modname) -- Constants -local DEBUG = false +local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", "Minecart Debug") local friction = mcl_minecarts.FRICTION +local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH -- Imports local train_length = mod.train_length @@ -131,6 +132,10 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) local meta = minetest.get_meta(vector.add(pos,next_dir)) if not cart_uuid then return end + + -- Don't collide with the train car in front of you + if cart1._staticdata.ahead == cart_uuid then return end + minetest.log("action","cart #"..cart1._staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos)) local cart2_aoid = mcl_util.get_active_object_id_from_uuid(cart_uuid) @@ -146,8 +151,8 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) local m1 = cart1_staticdata.mass local m2 = cart2_staticdata.mass - print("u1="..tostring(u1)..",u2="..tostring(u2)) - if u2 == 0 and u1 < 4 and train_length(cart1) < 3 then + --print("u1="..tostring(u1)..",u2="..tostring(u2)) + if u2 == 0 and u1 < 4 and train_length(cart1) < MAX_TRAIN_LENGTH then link_cart_ahead(cart1, cart2) cart2_staticdata.dir = mcl_minecarts:get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) cart2_staticdata.velocity = cart1_staticdata.velocity @@ -275,7 +280,7 @@ local function do_movement_step(self, dtime) end if DEBUG and ( v_0 > 0 or a ~= 0 ) then - print( " cart "..tostring(staticdata.uuid).. + mcl_debug(" cart "..tostring(staticdata.uuid).. ": a="..tostring(a).. ",v_0="..tostring(v_0).. ",x_0="..tostring(x_0).. @@ -334,7 +339,7 @@ local function do_movement_step(self, dtime) staticdata.distance = x_1 if DEBUG and (1==0) and ( v_0 > 0 or a ~= 0 ) then - print( "- cart #"..tostring(staticdata.uuid).. + mcl_debug( "- cart #"..tostring(staticdata.uuid).. ": a="..tostring(a).. ",v_0="..tostring(v_0).. ",v_1="..tostring(v_1).. @@ -362,7 +367,7 @@ local function do_movement_step(self, dtime) -- Get the next direction local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) if DEBUG and next_dir ~= staticdata.dir then - print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) + mcl_debug( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) end -- Handle cart collisions @@ -376,7 +381,7 @@ local function do_movement_step(self, dtime) -- Handle end of track if next_dir == staticdata.dir * -1 and next_dir.y == 0 then - if DEBUG then print("Stopping cart at end of track at "..tostring(pos)) end + if DEBUG then mcl_debug("Stopping cart at end of track at "..tostring(pos)) end staticdata.velocity = 0 end @@ -384,7 +389,7 @@ local function do_movement_step(self, dtime) staticdata.dir = next_dir elseif stops_in_block and v_1 < (friction/5) and a <= 0 then -- Handle direction flip due to gravity - if DEBUG then print("Gravity flipped direction") end + if DEBUG then mcl_debug("Gravity flipped direction") end -- Velocity should be zero at this point staticdata.velocity = 0 @@ -405,7 +410,7 @@ local function do_movement_step(self, dtime) -- Debug reporting if DEBUG and ( v_0 > 0 or v_1 > 0 ) then - print( " cart #"..tostring(staticdata.uuid).. + mcl_debug( " cart #"..tostring(staticdata.uuid).. ": a="..tostring(a).. ",v_0="..tostring(v_0).. ",v_1="..tostring(v_1).. diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index e9511a83d..c3cfdc187 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -4,7 +4,9 @@ local mod = mcl_minecarts -- Imports local get_cart_data = mod.get_cart_data +local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH +-- Follow .behind to the back end of a train local function find_back(start) while start.behind do local nxt = get_cart_data(start.behind) @@ -14,10 +16,13 @@ local function find_back(start) return start end +-- Iterate across all the cars in a train local function train_cars(anchor) local back = find_back(anchor._staticdata) + local limit = MAX_TRAIN_LENGTH return function() - if not back then return end + if not back or limit <= 0 then return end + limit = limit - 1 local ret = back if back.ahead then @@ -28,26 +33,6 @@ local function train_cars(anchor) return ret end end - -function mod.update_train(cart) - local sum_velocity = 0 - local count = 0 - for cart in train_cars(cart) do - count = count + 1 - sum_velocity = sum_velocity + (cart.velocity or 0) - end - local avg_velocity = sum_velocity / count - if count == 0 then return end - - print("Using velocity "..tostring(avg_velocity)) - - -- Set the entire train to the average velocity - for c in train_cars(cart) do - print(tostring(c.behind).."->"..c.uuid.."->"..tostring(c.ahead).." setting cart #"..c.uuid.." velocity to "..tostring(avg_velocity)) - c.velocity = avg_velocity - end -end - function mod.train_length(cart) local count = 0 for cart in train_cars(cart) do @@ -55,6 +40,68 @@ function mod.train_length(cart) end return count end + +function mod.is_in_same_train(anchor, other) + for cart in train_cars(anchor) do + if cart.uuid == other.uuid then return true end + end + return false +end + +function mod.distance_between_cars(car1, car2) + if not car1.connected_at then return nil end + if not car2.connected_at then return nil end + + if not car1.dir then car1.dir = vector.zero() end + if not car2.dir then car2.dir = vector.zero() end + + local pos1 = vector.add(car1.connected_at, vector.multiply(car1.dir, car1.distance)) + local pos2 = vector.add(car2.connected_at, vector.multiply(car2.dir, car2.distance)) + + return vector.distance(pos1, pos2) +end +local distance_between_cars = mod.distance_between_cars + +function mod.update_train(cart) + local staticdata = cart._staticdata + + -- Only update from the back + if staticdata.behind or not staticdata.ahead then return end + print("\nUpdating train") + + -- Do no special processing if the cart is not part of a train + if not staticdata.ahead and not staticdata.behind then return end + + -- Calculate the average velocity of all train cars + local sum_velocity = 0 + for cart in train_cars(cart) do + if cart.velocity or 0 > velocity then + velocity = cart.velocity + end + end + print("Using velocity "..tostring(velocity)) + + -- Set the entire train to the average velocity + local behind = nil + for c in train_cars(cart) do + local e = 0 + local separation + if behind then + separation = distance_between_cars(behind, c) + local e = 0 + if separation > 1.25 then + velocity = velocity * 0.9 + elseif separation < 1.15 then + velocity = velocity * 1.1 + end + end + print(tostring(c.behind).."->"..c.uuid.."->"..tostring(c.ahead).."("..tostring(separation)..") setting cart #"..c.uuid.." velocity to "..tostring(velocity)) + c.velocity = velocity + + behind = c + end +end + function mod.link_cart_ahead(cart, cart_ahead) local staticdata = cart._staticdata local ca_staticdata = cart_ahead._staticdata @@ -64,9 +111,3 @@ function mod.link_cart_ahead(cart, cart_ahead) staticdata.ahead = ca_staticdata.uuid ca_staticdata.behind = staticdata.uuid end -function mod.is_in_same_train(anchor, other) - for cart in train_cars(anchor) do - if cart.uuid == other.uuid then return true end - end - return false -end From d6e6194316a5d7d1ea03a55c6b073a9cf3613e40 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 7 Apr 2024 12:27:53 +0000 Subject: [PATCH 069/195] Add cart entity respawn/destroy to match cart data (partially working) --- mods/ENTITIES/mcl_minecarts/carts.lua | 73 ++++++++++++++++++++++- mods/ENTITIES/mcl_minecarts/functions.lua | 6 ++ mods/ENTITIES/mcl_minecarts/init.lua | 1 + mods/ENTITIES/mcl_minecarts/storage.lua | 28 ++++++++- 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index f0e4fab65..73f106201 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -6,11 +6,13 @@ local S = minetest.get_translator(modname) local mcl_log = mcl_util.make_mcl_logger("mcl_logging_minecarts", "Minecarts") -- Imports +local CART_BLOCK_SIZE = mod.CART_BLOCK_SIZE local table_merge = mcl_util.table_merge local get_cart_data = mod.get_cart_data local save_cart_data = mod.save_cart_data local update_cart_data = mod.update_cart_data local destroy_cart_data = mod.destroy_cart_data +local find_carts_by_block_map = mod.find_carts_by_block_map local do_movement,do_detached_movement,handle_cart_enter = dofile(modpath.."/movement.lua") assert(do_movement) assert(do_detached_movement) @@ -165,9 +167,15 @@ function DEFAULT_CART_DEF:on_step(dtime) self._staticdata = staticdata end + -- Repair cart_type + if not staticdata.cart_type then + staticdata.cart_type = self.name + end + if self._seq ~= staticdata.seq then - print("TODO: remove cart #"..staticdata.uuid.." with sequence number mismatch") - print("self.seq="..tostring(self._seq)..", staticdata.seq="..tostring(staticdata.seq)) + print("removing cart #"..staticdata.uuid.." with sequence number mismatch") + self.object:remove() + return end -- Regen @@ -309,6 +317,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) local uuid = mcl_util.get_uuid(cart) data = make_staticdata( railtype, railpos, cart_dir ) data.uuid = uuid + data.cart_type = entity_id update_cart_data(data) le._staticdata = data save_cart_data(le._staticdata.uuid) @@ -447,7 +456,67 @@ if minetest.get_modpath("mcl_wip") then mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart") end +local function respawn_cart(cart) + local cart_type = cart.cart_type or "mcl_minecarts:minecart" + local pos = mod.get_cart_position(cart) + print("Respawning cart #"..cart.uuid.." at "..tostring(pos)) + + -- Update sequence so that old cart entities get removed + cart.seq = (cart.seq or 1) + 1 + save_cart_data(cart.uuid) + + -- Create the new entity + local entity = minetest.add_entity(pos, cart_type) + local le = entity:get_luaentity() + le._seq = cart.seq + le._uuid = cart.uuid + le._staticdata = cart + + -- We intentionally don't call the normal hooks because this minecart was already there +end + +-- Try to respawn cart entities for carts that have moved into range of a player +local function try_respawn_carts() + -- Build a map of blocks near players + local block_map = {} + local players = minetest.get_connected_players() + for _,player in pairs(players) do + local pos = player:get_pos() + local min = vector.floor(vector.divide(vector.offset(pos,-CART_BLOCK_SIZE,-CART_BLOCK_SIZE,-CART_BLOCK_SIZE), CART_BLOCK_SIZE)) + local max = vector.floor(vector.divide(vector.offset(pos, CART_BLOCK_SIZE, CART_BLOCK_SIZE, CART_BLOCK_SIZE), CART_BLOCK_SIZE)) + vector.new(1,1,1) + for z = min.z,max.z do + for y = min.y,max.y do + for x = min.x,min.x do + block_map[ vector.to_string(vector.new(x,y,z)) ] = true + end + end + end + end + + -- Find all cart data that are in these blocks + local carts = find_carts_by_block_map(block_map) + + -- Check to see if any of these don't have an entity + for _,cart in pairs(carts) do + local le = mcl_util.get_luaentity_from_uuid(cart.uuid) + if not le then + respawn_cart(cart) + end + end +end + +local timer = 0 minetest.register_globalstep(function(dtime) + timer = timer - dtime + if timer <= 0 then + local start_time = minetest.get_us_time() + try_respawn_carts() + local stop_time = minetest.get_us_time() + local duration = (stop_time - start_time) / 1e6 + timer = duration / 50e-6 -- Schedule 50us per second + --print("Took "..tostring(duration).." seconds, rescheduling for "..tostring(timer).." seconds in the future") + end + -- TODO: handle periodically updating out-of-range carts end) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 6b8d067bc..53cc6b74c 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -356,4 +356,10 @@ function mcl_minecarts:update_cart_orientation() self.object:set_rotation(rot) end +function mod.get_cart_position(cart_staticdata) + local data = cart_staticdata + if not data.connected_at then return end + + return vector.add(data.connected_at, vector.multiply(data.dir or vector.zero(), data.distance or 0)) +end diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 22c67de5b..55f348544 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -9,6 +9,7 @@ mod.speed_max = 10 mod.check_float_time = 15 mod.FRICTION = 0.4 mod.MAX_TRAIN_LENGTH = 4 +mod.CART_BLOCK_SIZE = 64 for _,filename in pairs({"storage","functions","rails","train","carts"}) do dofile(modpath.."/"..filename..".lua") diff --git a/mods/ENTITIES/mcl_minecarts/storage.lua b/mods/ENTITIES/mcl_minecarts/storage.lua index f4ee1af51..adc7f23a6 100644 --- a/mods/ENTITIES/mcl_minecarts/storage.lua +++ b/mods/ENTITIES/mcl_minecarts/storage.lua @@ -1,10 +1,14 @@ local storage = minetest.get_mod_storage() local mod = mcl_minecarts +-- Imports +local CART_BLOCK_SIZE = mod.CART_BLOCK_SIZE + local cart_data = {} local cart_data_fail_cache = {} +local cart_ids = storage:get_keys() -function mod.get_cart_data(uuid) +local function get_cart_data(uuid) if cart_data[uuid] then return cart_data[uuid] end if cart_data_fail_cache[uuid] then return nil end @@ -17,6 +21,14 @@ function mod.get_cart_data(uuid) cart_data[uuid] = data return data end +mod.get_cart_data = get_cart_data + +-- Preload all cart data into memory +for _,id in pairs(cart_ids) do + local uuid = string.sub(id,6) + get_cart_data(uuid) +end + local function save_cart_data(uuid) if not cart_data[uuid] then return end storage:set_string("cart-"..uuid,minetest.serialize(cart_data[uuid])) @@ -35,3 +47,17 @@ function mod.destroy_cart_data(uuid) cart_data_fail_cache[uuid] = true end +function mod.find_carts_by_block_map(block_map) + local cart_list = {} + for _,data in pairs(cart_data) do + if data and data.connected_at then + local pos = mod.get_cart_position(data) + local block = vector.floor(vector.divide(pos,CART_BLOCK_SIZE)) + if block_map[vector.to_string(block)] then + cart_list[#cart_list + 1] = data + end + end + end + return cart_list +end + From 88ed70ce92dec50dce214d4f5a4c0209c54f62b0 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 8 Apr 2024 19:38:13 +0000 Subject: [PATCH 070/195] Repair vectors in cart data, mostly fix train movement bugs (still possible to have a furnace minecart flip, without the train also flipping) --- mods/ENTITIES/mcl_minecarts/functions.lua | 3 ++- mods/ENTITIES/mcl_minecarts/movement.lua | 12 +++++------- mods/ENTITIES/mcl_minecarts/storage.lua | 7 +++++++ mods/ENTITIES/mcl_minecarts/train.lua | 23 +++++++++++++---------- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 53cc6b74c..fad6121c0 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -358,7 +358,8 @@ end function mod.get_cart_position(cart_staticdata) local data = cart_staticdata - if not data.connected_at then return end + if not data then return nil end + if not data.connected_at then return nil end return vector.add(data.connected_at, vector.multiply(data.dir or vector.zero(), data.distance or 0)) end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 25db9a621..b316d0742 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -14,6 +14,7 @@ local update_train = mod.update_train local link_cart_ahead = mod.link_cart_ahead local update_cart_orientation = mod.update_cart_orientation local get_cart_data = mod.get_cart_data +local get_cart_position = mod.get_cart_position local function detach_minecart(self) local staticdata = self._staticdata @@ -118,7 +119,8 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) local dirty = false for uuid,v in pairs(carts) do -- Clean up dead carts - if not get_cart_data(uuid) then + local data = get_cart_data(uuid) + if not data then carts[uuid] = nil dirty = true uuid = nil @@ -138,13 +140,9 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) minetest.log("action","cart #"..cart1._staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos)) - local cart2_aoid = mcl_util.get_active_object_id_from_uuid(cart_uuid) - local cart2 = minetest.luaentities[cart2_aoid] - if not cart2 then return end - -- Standard Collision Handling local cart1_staticdata = cart1._staticdata - local cart2_staticdata = cart2._staticdata + local cart2_staticdata = get_cart_data(cart_uuid) local u1 = cart1_staticdata.velocity local u2 = cart2_staticdata.velocity @@ -153,7 +151,7 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) --print("u1="..tostring(u1)..",u2="..tostring(u2)) if u2 == 0 and u1 < 4 and train_length(cart1) < MAX_TRAIN_LENGTH then - link_cart_ahead(cart1, cart2) + link_cart_ahead(cart1, {_staticdata=cart2_staticdata}) cart2_staticdata.dir = mcl_minecarts:get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) cart2_staticdata.velocity = cart1_staticdata.velocity return diff --git a/mods/ENTITIES/mcl_minecarts/storage.lua b/mods/ENTITIES/mcl_minecarts/storage.lua index adc7f23a6..b31433c1d 100644 --- a/mods/ENTITIES/mcl_minecarts/storage.lua +++ b/mods/ENTITIES/mcl_minecarts/storage.lua @@ -16,6 +16,13 @@ local function get_cart_data(uuid) if not data then cart_data_fail_cache[uuid] = true return nil + else + -- Repair broken data + if not data.distance then data.distance = 0 end + if data.distance == 0/0 then data.distance = 0 end + if data.distance == -0/0 then data.distance = 0 end + data.dir = vector.new(data.dir) + data.connected_at = vector.new(data.connected_at) end cart_data[uuid] = data diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index c3cfdc187..88adb7f87 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -72,13 +72,14 @@ function mod.update_train(cart) -- Do no special processing if the cart is not part of a train if not staticdata.ahead and not staticdata.behind then return end - -- Calculate the average velocity of all train cars - local sum_velocity = 0 + -- Calculate the maximum velocity of all train cars + local velocity = 0 + local count = 0 for cart in train_cars(cart) do - if cart.velocity or 0 > velocity then - velocity = cart.velocity - end + velocity = velocity + (cart.velocity or 0) + count = count + 1 end + velocity = velocity / count print("Using velocity "..tostring(velocity)) -- Set the entire train to the average velocity @@ -86,17 +87,19 @@ function mod.update_train(cart) for c in train_cars(cart) do local e = 0 local separation + local cart_velocity = velocity if behind then separation = distance_between_cars(behind, c) local e = 0 - if separation > 1.25 then - velocity = velocity * 0.9 + if separation > 1.6 then + cart_velocity = velocity * 0.9 elseif separation < 1.15 then - velocity = velocity * 1.1 + cart_velocity = velocity * 1.1 end end - print(tostring(c.behind).."->"..c.uuid.."->"..tostring(c.ahead).."("..tostring(separation)..") setting cart #"..c.uuid.." velocity to "..tostring(velocity)) - c.velocity = velocity + print(tostring(c.behind).."->"..c.uuid.."->"..tostring(c.ahead).."("..tostring(separation)..") setting cart #".. + c.uuid.." velocity from "..tostring(c.velocity).." to "..tostring(cart_velocity)) + c.velocity = cart_velocity behind = c end From d280e2e942d783e161e19e9e8237214aad9ac8cd Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 8 Apr 2024 21:37:54 +0000 Subject: [PATCH 071/195] Implement train reversing --- mods/ENTITIES/mcl_minecarts/functions.lua | 12 ++++++++++++ mods/ENTITIES/mcl_minecarts/movement.lua | 13 ++++++------- mods/ENTITIES/mcl_minecarts/train.lua | 9 +++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index fad6121c0..a3b256022 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -364,3 +364,15 @@ function mod.get_cart_position(cart_staticdata) return vector.add(data.connected_at, vector.multiply(data.dir or vector.zero(), data.distance or 0)) end +function mod.reverse_cart_direction(staticdata) + + -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at + local next_dir = -staticdata.dir + staticdata.connected_at = staticdata.connected_at + staticdata.dir + staticdata.distance = 1 - (staticdata.distance or 0) + + -- recalculate direction + local next_dir,_ = mod:get_rail_direction(staticdata.connected_at, next_dir, nil, nil, staticdata.railtype) + staticdata.dir = next_dir +end + diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index b316d0742..1b84504f1 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -11,6 +11,7 @@ local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH -- Imports local train_length = mod.train_length local update_train = mod.update_train +local reverse_train = mod.reverse_train local link_cart_ahead = mod.link_cart_ahead local update_cart_orientation = mod.update_cart_orientation local get_cart_data = mod.get_cart_data @@ -248,14 +249,12 @@ local function calculate_acceleration(self, staticdata) end local function reverse_direction(self, staticdata) - -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at - local next_dir = -staticdata.dir - staticdata.connected_at = staticdata.connected_at + staticdata.dir - staticdata.distance = 1 - (staticdata.distance or 0) + if staticdata.behind or staticdata.ahead then + reverse_train(self) + return + end - -- recalculate direction - local next_dir,_ = mcl_minecarts:get_rail_direction(staticdata.connected_at, next_dir, nil, nil, staticdata.railtype) - staticdata.dir = next_dir + mod.reverse_cart_direction(staticdata) end local function do_movement_step(self, dtime) diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index 88adb7f87..cac57fa7d 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -114,3 +114,12 @@ function mod.link_cart_ahead(cart, cart_ahead) staticdata.ahead = ca_staticdata.uuid ca_staticdata.behind = staticdata.uuid end + + +function mod.reverse_train(cart) + for c in train_cars(cart) do + mod.reverse_cart_direction(c) + c.behind,c.ahead = c.ahead,c.behind + end +end + From 2e41f2df48e4d5ccd5c931d0cd273abc2673535a Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 9 Apr 2024 00:40:04 +0000 Subject: [PATCH 072/195] Fix crashes in train logic, allow breaking apart trains --- mods/ENTITIES/mcl_minecarts/movement.lua | 2 +- mods/ENTITIES/mcl_minecarts/train.lua | 32 ++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 1b84504f1..7210e22ce 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -121,7 +121,7 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) for uuid,v in pairs(carts) do -- Clean up dead carts local data = get_cart_data(uuid) - if not data then + if not data or not data.connected_at then carts[uuid] = nil dirty = true uuid = nil diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index cac57fa7d..b3648f026 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -4,6 +4,7 @@ local mod = mcl_minecarts -- Imports local get_cart_data = mod.get_cart_data +local save_cart_data = mod.save_cart_data local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH -- Follow .behind to the back end of a train @@ -62,6 +63,27 @@ function mod.distance_between_cars(car1, car2) end local distance_between_cars = mod.distance_between_cars +local function break_train_at(cart) + if cart.ahead then + local ahead = get_cart_data(cart.ahead) + if ahead then + ahead.behind = nil + cart.ahead = nil + save_cart_data(ahead.uuid) + end + end + if cart.behind then + local behind = get_cart_data(cart.behind) + if behind then + behind.ahead = nil + cart.behind = nil + save_cart_data(behind.uuid) + end + end + save_cart_data(cart.uuid) +end +mod.break_train_at = break_train_at + function mod.update_train(cart) local staticdata = cart._staticdata @@ -88,11 +110,17 @@ function mod.update_train(cart) local e = 0 local separation local cart_velocity = velocity - if behind then + if not c.connected_at then + break_train_at(c) + elseif behind then separation = distance_between_cars(behind, c) local e = 0 - if separation > 1.6 then + if not separation then + break_train_at(c) + elseif separation > 1.6 then cart_velocity = velocity * 0.9 + elseif separation > 2.5 then + break_train_at(c) elseif separation < 1.15 then cart_velocity = velocity * 1.1 end From 265a25d6ae107a554fb17b28760f71b3377a9043 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 9 Apr 2024 07:35:57 +0000 Subject: [PATCH 073/195] Make trains containing a player in a minecart function, minor cleanup in mcl_playerinfo --- mods/ENTITIES/mcl_minecarts/carts.lua | 17 ++++++- .../ENTITIES/mcl_minecarts/carts/minecart.lua | 48 +++++++++++-------- mods/ENTITIES/mcl_minecarts/mod.conf | 2 +- mods/ENTITIES/mcl_minecarts/movement.lua | 18 +++++-- mods/ENTITIES/mcl_minecarts/train.lua | 6 ++- mods/PLAYER/mcl_playerinfo/init.lua | 7 +-- 6 files changed, 63 insertions(+), 35 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 73f106201..36c6d30c2 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -27,10 +27,23 @@ local function detach_driver(self) if not self._driver then return end - mcl_player.player_attached[self._driver] = nil - local player = minetest.get_player_by_name(self._driver) + + -- Update player infomation + local driver_name = self._driver + local playerinfo = mcl_playerinfo[driver_name] + if playerinfo then + playerinfo.attached_to = nil + end + mcl_player.player_attached[driver_name] = nil + + minetest.log("action", driver_name.." left a minecart") + + -- Update cart informatino self._driver = nil self._start_pos = nil + + -- Detatch the player object from the minecart + local player = minetest.get_player_by_name(driver_name) if player then player:set_detach() player:set_eye_offset(vector.new(0,0,0),vector.new(0,0,0)) diff --git a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua index a28b0a8d3..b711155b3 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua @@ -1,5 +1,6 @@ local modname = minetest.get_current_modname() local S = minetest.get_translator(modname) +local mcl_log = mcl_util.make_mcl_logger("mcl_logging_minecarts", "Minecarts") local function activate_normal_minecart(self) detach_driver(self) @@ -35,28 +36,35 @@ mcl_minecarts.register_minecart({ icon = "mcl_minecarts_minecart_normal.png", drop = {"mcl_minecarts:minecart"}, on_rightclick = function(self, clicker) - local name = clicker:get_player_name() - if not clicker or not clicker:is_player() then - return - end + -- Make sure we have a player + if not clicker or not clicker:is_player() then return end + local player_name = clicker:get_player_name() - if self._driver and player_name == self._driver then - --detach_driver(self) - elseif not self._driver and not clicker:get_player_control().sneak then - self._driver = player_name - self._start_pos = self.object:get_pos() - mcl_player.player_attached[player_name] = true - clicker:set_attach(self.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) - mcl_player.player_attached[name] = true - minetest.after(0.2, function(name) - local player = minetest.get_player_by_name(name) - if player then - mcl_player.player_set_animation(player, "sit" , 30) - player:set_eye_offset(vector.new(0,-5.5,0), vector.new(0,-4,0)) - mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) - end - end, name) + if self._driver or clicker:get_player_control().sneak then return end + + -- Update cart information + self._driver = player_name + self._start_pos = self.object:get_pos() + + -- Update player information + local uuid = self._staticdata.uuid + local playerinfo = mcl_playerinfo[player_name] + if playerinfo and self._staticdata then + playerinfo.attached_to = uuid end + mcl_player.player_attached[player_name] = true + minetest.log("action", player_name.." entered minecart #"..tostring(uuid).." at "..tostring(self._start_pos)) + + -- Attach the player object to the minecart + clicker:set_attach(self.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) + minetest.after(0.2, function(name) + local player = minetest.get_player_by_name(name) + if player then + mcl_player.player_set_animation(player, "sit" , 30) + player:set_eye_offset(vector.new(0,-5.5,0), vector.new(0,-4,0)) + mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) + end + end, player_name) end, on_activate_by_rail = activate_normal_minecart, _mcl_minecarts_on_step = function(self, dtime) diff --git a/mods/ENTITIES/mcl_minecarts/mod.conf b/mods/ENTITIES/mcl_minecarts/mod.conf index 994abe809..29959b322 100644 --- a/mods/ENTITIES/mcl_minecarts/mod.conf +++ b/mods/ENTITIES/mcl_minecarts/mod.conf @@ -1,5 +1,5 @@ name = mcl_minecarts author = Krock description = Minecarts are vehicles to move players quickly on rails. -depends = mcl_title, mcl_explosions, mcl_core, mcl_util, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs +depends = mcl_title, mcl_explosions, mcl_core, mcl_util, mcl_sounds, mcl_player, mcl_playerinfo, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs optional_depends = doc_identifier, mcl_wip, mcl_physics diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 7210e22ce..903cd0516 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -171,13 +171,21 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) cart2_staticdata.dir = mcl_minecarts:get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) end - local function vector_away_from_players(self, staticdata) - local objs = minetest.get_objects_inside_radius(self.object:get_pos(), 1.1) - for n=1,#objs do - local obj = objs[n] + local function player_repel(obj, self) + -- Only repel from players local player_name = obj:get_player_name() - if player_name and player_name ~= "" and not ( self._driver and self._driver == player_name ) then + if not player_name or player_name == "" then return false end + + -- Don't repel away from players in minecarts + local playerinfo = mcl_playerinfo[player_name] + if playerinfo and playerinfo.attached_to then return false end + + return true + end + + for _,obj in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 1.1)) do + if player_repel(obj, self) then return obj:get_pos() - self.object:get_pos() end end diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index b3648f026..c2594363c 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -89,7 +89,7 @@ function mod.update_train(cart) -- Only update from the back if staticdata.behind or not staticdata.ahead then return end - print("\nUpdating train") + --print("\nUpdating train") -- Do no special processing if the cart is not part of a train if not staticdata.ahead and not staticdata.behind then return end @@ -102,7 +102,7 @@ function mod.update_train(cart) count = count + 1 end velocity = velocity / count - print("Using velocity "..tostring(velocity)) + --print("Using velocity "..tostring(velocity)) -- Set the entire train to the average velocity local behind = nil @@ -125,8 +125,10 @@ function mod.update_train(cart) cart_velocity = velocity * 1.1 end end + --[[ print(tostring(c.behind).."->"..c.uuid.."->"..tostring(c.ahead).."("..tostring(separation)..") setting cart #".. c.uuid.." velocity from "..tostring(c.velocity).." to "..tostring(cart_velocity)) + ]] c.velocity = cart_velocity behind = c diff --git a/mods/PLAYER/mcl_playerinfo/init.lua b/mods/PLAYER/mcl_playerinfo/init.lua index 1f1b84749..f15f46b36 100644 --- a/mods/PLAYER/mcl_playerinfo/init.lua +++ b/mods/PLAYER/mcl_playerinfo/init.lua @@ -21,8 +21,6 @@ local function node_ok(pos, fallback) return fallback end -local time = 0 - local function get_player_nodes(player_pos) local work_pos = table.copy(player_pos) @@ -43,11 +41,10 @@ local function get_player_nodes(player_pos) return node_stand, node_stand_below, node_head, node_feet, node_head_top end +local time = 0 minetest.register_globalstep(function(dtime) - - time = time + dtime - -- Run the rest of the code every 0.5 seconds + time = time + dtime if time < 0.5 then return end From 7c7a987342cce16f4d09b9c0aaacfcd4aedaf4c8 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 9 Apr 2024 08:14:30 +0000 Subject: [PATCH 074/195] Fix undefined global warning, move player off to the side of a cart when dismounting so trains don't get pushed apart when getting out --- mods/ENTITIES/mcl_minecarts/carts.lua | 16 +++++++++++++++- mods/ENTITIES/mcl_minecarts/carts/minecart.lua | 6 +++++- mods/ENTITIES/mcl_minecarts/init.lua | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 36c6d30c2..b608e1708 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -21,9 +21,10 @@ assert(handle_cart_enter) -- Constants local max_step_distance = 0.5 local MINECART_MAX_HP = 4 -local PASSENGER_ATTACH_POSITION = vector.new(0, -1.75, 0) local function detach_driver(self) + local staticdata = self._staticdata + if not self._driver then return end @@ -45,9 +46,22 @@ local function detach_driver(self) -- Detatch the player object from the minecart local player = minetest.get_player_by_name(driver_name) if player then + local dir = staticdata.dir or vector.new(1,0,0) + local cart_pos = mod.get_cart_position(staticdata) or self.object:get_pos() + local new_pos = vector.offset(cart_pos, -dir.z, 0, dir.x) player:set_detach() + print("placing player at "..tostring(new_pos).." from cart at "..tostring(cart_pos)..", old_pos="..tostring(player:get_pos()).."dir="..tostring(dir)) + + -- There needs to be a delay here or the player's position won't update + minetest.after(0.1,function(driver_name,new_pos) + local player = minetest.get_player_by_name(driver_name) + player:moveto(new_pos, false) + end, driver_name, new_pos) + player:set_eye_offset(vector.new(0,0,0),vector.new(0,0,0)) mcl_player.player_set_animation(player, "stand" , 30) + else + print("No player object found for "..driver_name) end end diff --git a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua index b711155b3..bf3fce514 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua @@ -1,6 +1,10 @@ local modname = minetest.get_current_modname() local S = minetest.get_translator(modname) local mcl_log = mcl_util.make_mcl_logger("mcl_logging_minecarts", "Minecarts") +local mod = mcl_minecarts + +-- Imports +local PASSENGER_ATTACH_POSITION = mod.PASSENGER_ATTACH_POSITION local function activate_normal_minecart(self) detach_driver(self) @@ -12,7 +16,7 @@ local function activate_normal_minecart(self) end end -mcl_minecarts.register_minecart({ +mod.register_minecart({ itemstring = "mcl_minecarts:minecart", craft = { output = "mcl_minecarts:minecart", diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 55f348544..6a6f93318 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -10,6 +10,7 @@ mod.check_float_time = 15 mod.FRICTION = 0.4 mod.MAX_TRAIN_LENGTH = 4 mod.CART_BLOCK_SIZE = 64 +mod.PASSENGER_ATTACH_POSITION = vector.new(0, -1.75, 0) for _,filename in pairs({"storage","functions","rails","train","carts"}) do dofile(modpath.."/"..filename..".lua") From a500f554e0b8344a3dfa78c0cedb7b80c9481f87 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 9 Apr 2024 11:59:45 +0000 Subject: [PATCH 075/195] Remove do_movement dependency on the existence of a cart luaentity --- mods/ENTITIES/mcl_minecarts/carts.lua | 7 +- mods/ENTITIES/mcl_minecarts/movement.lua | 116 ++++++++++++----------- mods/ENTITIES/mcl_minecarts/train.lua | 17 ++-- mods/ITEMS/mcl_hoppers/init.lua | 5 + 4 files changed, 82 insertions(+), 63 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index b608e1708..828a275d5 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -257,7 +257,12 @@ function DEFAULT_CART_DEF:on_step(dtime) end if staticdata.connected_at then - do_movement(self, dtime) + do_movement(staticdata, dtime) + + -- Update entity + local pos = mod.get_cart_position(staticdata) + if pos then self.object:move_to(pos) end + mod.update_cart_orientation(self) else do_detached_movement(self, dtime) end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 903cd0516..bccbb5d83 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -7,6 +7,8 @@ local S = minetest.get_translator(modname) local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", "Minecart Debug") local friction = mcl_minecarts.FRICTION local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH +DEBUG = true +mcl_debug = function(msg) print(msg) end -- Imports local train_length = mod.train_length @@ -17,16 +19,17 @@ local update_cart_orientation = mod.update_cart_orientation local get_cart_data = mod.get_cart_data local get_cart_position = mod.get_cart_position -local function detach_minecart(self) - local staticdata = self._staticdata - +local function detach_minecart(staticdata) staticdata.connected_at = nil - self.object:set_velocity(staticdata.dir * staticdata.velocity) + + local luaentity = mcl_util.get_luaentity_from_uuid(staticdata.uuid) + if luaentity then + luaentity.object:set_velocity(staticdata.dir * staticdata.velocity) + end end mod.detach_minecart = detach_minecart -local function try_detach_minecart(self) - local staticdata = self._staticdata +local function try_detach_minecart(staticdata) if not staticdata then return end -- Don't try to detach if alread detached @@ -34,7 +37,7 @@ local function try_detach_minecart(self) local node = minetest.get_node(staticdata.connected_at) if minetest.get_item_group(node.name, "rail") == 0 then - detach_minecart(self) + detach_minecart(staticdata) end end @@ -50,9 +53,8 @@ local enter_exit_checks = { { 0,-1, 0, "_side" }, } -local function handle_cart_enter_exit(self, pos, next_dir, event) - local staticdata = self._staticdata - +local function handle_cart_enter_exit(staticdata, pos, next_dir, event) + local luaentity = mcl_util.get_luaentity_from_uuid(staticdata.uuid) local dir = staticdata.dir local right = vector.new( dir.z, dir.y, -dir.x) local up = vector.new(0,1,0) @@ -64,16 +66,20 @@ local function handle_cart_enter_exit(self, pos, next_dir, event) -- node-specific hook local hook_name = "_mcl_minecarts_"..event..check[4] local hook = node_def[hook_name] - if hook then hook(check_pos, self, next_dir, pos) end + if hook then hook(check_pos, luaentity, next_dir, pos) end -- global minecart hook hook = mcl_minecarts[event..check[4]] - if hook then hook(check_pos, self, next_dir, node_def) end + if hook then hook(check_pos, luaentity, next_dir, node_def) end end end -- Handle cart-specific behaviors - local hook = self["_mcl_minecarts_"..event] + if luaentity then + local hook = luaentity["_mcl_minecarts_"..event] + else + minetest.log("warning", "TODO: chanve _mcl_minecarts_"..event.." calling so it is not dependent on the existence of a luaentity") + end if hook then hook(self, pos) end end local function set_metadata_cart_status(pos, uuid, state) @@ -82,26 +88,26 @@ local function set_metadata_cart_status(pos, uuid, state) carts[uuid] = state meta:set_string("_mcl_minecarts_carts", minetest.serialize(carts)) end -local function handle_cart_enter(self, pos, next_dir) +local function handle_cart_enter(staticdata, pos, next_dir) --print("entering "..tostring(pos)) - set_metadata_cart_status(pos, self._staticdata.uuid, 1) - handle_cart_enter_exit(self, pos, next_dir, "on_enter" ) + set_metadata_cart_status(pos, staticdata.uuid, 1) + handle_cart_enter_exit(staticdata, pos, next_dir, "on_enter" ) end -local function handle_cart_leave(self, pos, next_dir) +local function handle_cart_leave(staticdata, pos, next_dir) --print("leaving "..tostring(pos)) - set_metadata_cart_status(pos, self._staticdata.uuid, nil) - handle_cart_enter_exit(self, pos, next_dir, "on_leave" ) + set_metadata_cart_status(pos, staticdata.uuid, nil) + handle_cart_enter_exit(staticdata, pos, next_dir, "on_leave" ) end -local function handle_cart_node_watches(self, dtime) - local staticdata = self._staticdata +local function handle_cart_node_watches(staticdata, dtime) local watches = staticdata.node_watches or {} local new_watches = {} + local luaentity = mcl_util.get_luaentity_from_uuid(staticdata.uuid) for _,node_pos in ipairs(watches) do local node = minetest.get_node(node_pos) local node_def = minetest.registered_nodes[node.name] if node_def then local hook = node_def._mcl_minecarts_node_on_step - if hook and hook(node_pos, self, dtime) then + if hook and hook(node_pos, luaentity, dtime) then new_watches[#new_watches+1] = node_pos end end @@ -110,7 +116,7 @@ local function handle_cart_node_watches(self, dtime) staticdata.node_watches = new_watches end -local function handle_cart_collision(cart1, prev_pos, next_dir) +local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) -- Look ahead one block local pos = vector.add(prev_pos, next_dir) @@ -127,7 +133,7 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) uuid = nil end - if uuid and uuid ~= cart1._staticdata.uuid then cart_uuid = uuid end + if uuid and uuid ~= cart1_staticdata.uuid then cart_uuid = uuid end end if dirty then meta:set_string("_mcl_minecarts_carts",minetest.serialize(carts)) @@ -137,12 +143,11 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) if not cart_uuid then return end -- Don't collide with the train car in front of you - if cart1._staticdata.ahead == cart_uuid then return end + if cart1_staticdata.ahead == cart_uuid then return end - minetest.log("action","cart #"..cart1._staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos)) + minetest.log("action","cart #"..cart1_staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos)) -- Standard Collision Handling - local cart1_staticdata = cart1._staticdata local cart2_staticdata = get_cart_data(cart_uuid) local u1 = cart1_staticdata.velocity @@ -171,8 +176,8 @@ local function handle_cart_collision(cart1, prev_pos, next_dir) cart2_staticdata.dir = mcl_minecarts:get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) end -local function vector_away_from_players(self, staticdata) - local function player_repel(obj, self) +local function vector_away_from_players(cart, staticdata) + local function player_repel(obj) -- Only repel from players local player_name = obj:get_player_name() if not player_name or player_name == "" then return false end @@ -184,8 +189,13 @@ local function vector_away_from_players(self, staticdata) return true end - for _,obj in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 1.1)) do - if player_repel(obj, self) then + -- Get the cart position + local cart_pos = mod.get_cart_position(staticdata) + if cart then cart_pos = cart.object:get_pos() end + if not cart_pos then return nil end + + for _,obj in pairs(minetest.get_objects_inside_radius(cart_pos, 1.1)) do + if player_repel(obj) then return obj:get_pos() - self.object:get_pos() end end @@ -193,8 +203,8 @@ local function vector_away_from_players(self, staticdata) return nil end -local function direction_away_from_players(self, staticdata) - local diff = vector_away_from_players(self, staticdata) +local function direction_away_from_players(staticdata) + local diff = vector_away_from_players(staticdata) if not diff then return 0 end local length = vector.distance(vector.new(0,0,0),diff) @@ -217,7 +227,7 @@ local function direction_away_from_players(self, staticdata) return 0 end -local function calculate_acceleration(self, staticdata) +local function calculate_acceleration(staticdata) local acceleration = 0 -- Fix up movement data @@ -233,9 +243,11 @@ local function calculate_acceleration(self, staticdata) local node_def = minetest.registered_nodes[node_name] local max_vel = mcl_minecarts.speed_max - if self._go_forward then + local ctrl = staticdata.controls or {} + + if ctrl.go_forward then acceleration = 4 - elseif self._brake then + elseif ctrl.brake then acceleration = -1.5 elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then acceleration = 0.6 @@ -256,7 +268,7 @@ local function calculate_acceleration(self, staticdata) return acceleration end -local function reverse_direction(self, staticdata) +local function reverse_direction(staticdata) if staticdata.behind or staticdata.ahead then reverse_train(self) return @@ -265,18 +277,17 @@ local function reverse_direction(self, staticdata) mod.reverse_cart_direction(staticdata) end -local function do_movement_step(self, dtime) - local staticdata = self._staticdata +local function do_movement_step(staticdata, dtime) if not staticdata.connected_at then return 0 end -- Calculate timestep remaiing in this block local x_0 = staticdata.distance or 0 local remaining_in_block = 1 - x_0 - local a = calculate_acceleration(self, staticdata) + local a = calculate_acceleration(staticdata) local v_0 = staticdata.velocity -- Repel minecarts - local away = direction_away_from_players(self, staticdata) + local away = direction_away_from_players(nil, staticdata) if away > 0 then v_0 = away elseif away < 0 then @@ -379,10 +390,10 @@ local function do_movement_step(self, dtime) handle_cart_collision(self, pos, next_dir) -- Leave the old node - handle_cart_leave(self, old_pos, next_dir ) + handle_cart_leave(staticdata, old_pos, next_dir ) -- Enter the new node - handle_cart_enter(self, pos, next_dir) + handle_cart_enter(staticdata, pos, next_dir) -- Handle end of track if next_dir == staticdata.dir * -1 and next_dir.y == 0 then @@ -399,7 +410,7 @@ local function do_movement_step(self, dtime) -- Velocity should be zero at this point staticdata.velocity = 0 - reverse_direction(self, staticdata) + reverse_direction(staticdata) -- Intermediate movement pos = staticdata.connected_at + staticdata.dir * staticdata.distance @@ -408,11 +419,6 @@ local function do_movement_step(self, dtime) pos = pos + staticdata.dir * staticdata.distance end - self.object:move_to(pos) - - -- Update cart orientation - update_cart_orientation(self) - -- Debug reporting if DEBUG and ( v_0 > 0 or v_1 > 0 ) then mcl_debug( " cart #"..tostring(staticdata.uuid).. @@ -433,8 +439,8 @@ local function do_movement_step(self, dtime) return dtime - timestep end -local function do_movement( self, dtime ) - local staticdata = self._staticdata +local function do_movement( staticdata, dtime ) + assert(staticdata) -- Allow the carts to be delay for the rest of the world to react before moving again if ( staticdata.delay or 0 ) > dtime then @@ -448,13 +454,13 @@ local function do_movement( self, dtime ) -- it impossible to jump across gaps due to server lag -- causing large timesteps while dtime > 0 do - local new_dtime = do_movement_step(self, dtime) - try_detach_minecart(self) + local new_dtime = do_movement_step(staticdata, dtime) + try_detach_minecart(staticdata) - update_train(self) + update_train(staticdata) -- Handle node watches here in steps to prevent server lag from changing behavior - handle_cart_node_watches(self, dtime - new_dtime) + handle_cart_node_watches(staticdata, dtime - new_dtime) dtime = new_dtime end diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index c2594363c..d60678a09 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -9,6 +9,8 @@ local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH -- Follow .behind to the back end of a train local function find_back(start) + assert(start) + while start.behind do local nxt = get_cart_data(start.behind) if not nxt then return start end @@ -18,8 +20,10 @@ local function find_back(start) end -- Iterate across all the cars in a train -local function train_cars(anchor) - local back = find_back(anchor._staticdata) +local function train_cars(staticdata) + assert(staticdata) + + local back = find_back(staticdata) local limit = MAX_TRAIN_LENGTH return function() if not back or limit <= 0 then return end @@ -84,8 +88,8 @@ local function break_train_at(cart) end mod.break_train_at = break_train_at -function mod.update_train(cart) - local staticdata = cart._staticdata +function mod.update_train(staticdata) + --local staticdata = cart._staticdata -- Only update from the back if staticdata.behind or not staticdata.ahead then return end @@ -97,7 +101,7 @@ function mod.update_train(cart) -- Calculate the maximum velocity of all train cars local velocity = 0 local count = 0 - for cart in train_cars(cart) do + for cart in train_cars(staticdata) do velocity = velocity + (cart.velocity or 0) count = count + 1 end @@ -106,7 +110,7 @@ function mod.update_train(cart) -- Set the entire train to the average velocity local behind = nil - for c in train_cars(cart) do + for c in train_cars(staticdata) do local e = 0 local separation local cart_velocity = velocity @@ -145,7 +149,6 @@ function mod.link_cart_ahead(cart, cart_ahead) ca_staticdata.behind = staticdata.uuid end - function mod.reverse_train(cart) for c in train_cars(cart) do mod.reverse_cart_direction(c) diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index 97f99fd37..0ba1ca9a6 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -253,6 +253,11 @@ local def_hopper = { cart:remove_node_watch(pos) end, _mcl_minecarts_node_on_step = function(pos, cart, dtime) + if not cart then + minetest.log("warning", "trying to process hopper-to-minecart movement without luaentity") + return + end + local meta = minetest.get_meta(pos) local timer = meta:get_int("minecart_hopper_timer") From 4dccfff1d0bacb37e663a8267e6309bc721b6d9e Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 9 Apr 2024 20:52:11 +0000 Subject: [PATCH 076/195] Implement offline/out of range minecart movement and fix minecart respawning, remove railtype tracking --- mods/ENTITIES/mcl_minecarts/carts.lua | 114 ++++++++++++---------- mods/ENTITIES/mcl_minecarts/functions.lua | 2 +- mods/ENTITIES/mcl_minecarts/movement.lua | 22 ++--- mods/ENTITIES/mcl_minecarts/storage.lua | 9 ++ 4 files changed, 80 insertions(+), 67 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 828a275d5..e2e105285 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -68,9 +68,8 @@ end -- Table for item-to-entity mapping. Keys: itemstring, Values: Corresponding entity ID local entity_mapping = {} -local function make_staticdata( railtype, connected_at, dir ) +local function make_staticdata( _, connected_at, dir ) return { - railtype = railtype, connected_at = connected_at, distance = 0, velocity = 0, @@ -122,12 +121,6 @@ function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) -- Initialize if type(data) == "table" then - -- Migrate old data - if data._railtype then - data.railtype = data._railtype - data._railtype = nil - end - -- Fix up types data.dir = vector.new(data.dir) @@ -194,11 +187,17 @@ function DEFAULT_CART_DEF:on_step(dtime) self._staticdata = staticdata end + + -- Update entity position + local pos = mod.get_cart_position(staticdata) + if pos then self.object:move_to(pos) end + -- Repair cart_type if not staticdata.cart_type then staticdata.cart_type = self.name end + -- Remove superceded entities if self._seq ~= staticdata.seq then print("removing cart #"..staticdata.uuid.." with sequence number mismatch") self.object:remove() @@ -207,22 +206,11 @@ function DEFAULT_CART_DEF:on_step(dtime) -- Regen local hp = self.object:get_hp() - if hp < MINECART_MAX_HP then - if (staticdata.regen_timer or 0) > 0.5 then - hp = hp + 1 - staticdata.regen_timer = staticdata.regen_timer - 1 - end - staticdata.regen_timer = (staticdata.regen_timer or 0) + dtime + local time_now = minetest.get_gametime() + if hp < MINECART_MAX_HP and staticdata.last_regen <= time_now - 1 then + staticdata.last_regen = time_now + hp = hp + 1 self.object:set_hp(hp) - else - staticdata.regen_timer = nil - end - - -- Fix railtype field - local pos = self.object:get_pos() - if staticdata.connected_at and not staticdata.railtype then - local node = minetest.get_node(vector.floor(pos)).name - staticdata.railtype = minetest.get_item_group(node, "connect_to_raillike") end -- Cart specific behaviors @@ -256,27 +244,12 @@ function DEFAULT_CART_DEF:on_step(dtime) end end - if staticdata.connected_at then - do_movement(staticdata, dtime) - - -- Update entity - local pos = mod.get_cart_position(staticdata) - if pos then self.object:move_to(pos) end - mod.update_cart_orientation(self) - else + if not staticdata.connected_at then do_detached_movement(self, dtime) end - -- TODO: move this into mcl_core:cactus _mcl_minecarts_on_enter_side - -- Drop minecart if it collides with a cactus node - local r = 0.6 - for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do - if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then - self:on_death() - self.object:remove() - return - end - end + mod.update_cart_orientation(self) + end function DEFAULT_CART_DEF:on_death(killer) local staticdata = self._staticdata @@ -325,7 +298,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) local spawn_pos = pointed_thing.above local cart_dir = vector.new(1,0,0) - local railtype, railpos, node + local railpos, node if mcl_minecarts:is_rail(pointed_thing.under) then railpos = pointed_thing.under elseif mcl_minecarts:is_rail(pointed_thing.above) then @@ -334,8 +307,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) if railpos then spawn_pos = railpos node = minetest.get_node(railpos) - railtype = minetest.get_item_group(node.name, "connect_to_raillike") - cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0), nil, nil, railtype) + cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0)) end local entity_id = entity_mapping[itemstack:get_name()] @@ -343,16 +315,18 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) cart:set_yaw(minetest.dir_to_yaw(cart_dir)) + -- Setup cart data + local uuid = mcl_util.get_uuid(cart) + data = make_staticdata( nil, railpos, cart_dir ) + data.uuid = uuid + data.cart_type = entity_id + update_cart_data(data) + save_cart_data(uuid) + -- Update static data local le = cart:get_luaentity() if le then - local uuid = mcl_util.get_uuid(cart) - data = make_staticdata( railtype, railpos, cart_dir ) - data.uuid = uuid - data.cart_type = entity_id - update_cart_data(data) le._staticdata = data - save_cart_data(le._staticdata.uuid) end -- Call placer @@ -361,7 +335,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) end if railpos then - handle_cart_enter(le, railpos) + handle_cart_enter(data, railpos) end local pname = "" @@ -518,7 +492,7 @@ local function try_respawn_carts() local max = vector.floor(vector.divide(vector.offset(pos, CART_BLOCK_SIZE, CART_BLOCK_SIZE, CART_BLOCK_SIZE), CART_BLOCK_SIZE)) + vector.new(1,1,1) for z = min.z,max.z do for y = min.y,max.y do - for x = min.x,min.x do + for x = min.x,max.x do block_map[ vector.to_string(vector.new(x,y,z)) ] = true end end @@ -545,10 +519,42 @@ minetest.register_globalstep(function(dtime) try_respawn_carts() local stop_time = minetest.get_us_time() local duration = (stop_time - start_time) / 1e6 - timer = duration / 50e-6 -- Schedule 50us per second + timer = duration / 250e-6 -- Schedule 50us per second + if timer > 5 then timer = 5 end --print("Took "..tostring(duration).." seconds, rescheduling for "..tostring(timer).." seconds in the future") end - -- TODO: handle periodically updating out-of-range carts + -- Handle periodically updating out-of-range carts + -- TODO: change how often cart positions are updated based on velocity + for uuid,staticdata in mod.carts() do + local pos = mod.get_cart_position(staticdata) + local le = mcl_util.get_luaentity_from_uuid(staticdata.uuid) + --[[ + print("cart# "..uuid.. + ",velocity="..tostring(staticdata.velocity).. + ",pos="..tostring(pos).. + ",le="..tostring(le).. + ",connected_at="..tostring(staticdata.connected_at) + )]] + + --- Non-entity code + if staticdata.connected_at then + do_movement(staticdata, dtime) + + -- TODO: move this into mcl_core:cactus _mcl_minecarts_on_enter_side + -- Drop minecart if it collides with a cactus node + local pos = mod.get_cart_position(staticdata) + if pos then + local r = 0.6 + for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do + if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then + self:on_death() + self.object:remove() + return + end + end + end + end + end end) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index a3b256022..40d5cd2d3 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -372,7 +372,7 @@ function mod.reverse_cart_direction(staticdata) staticdata.distance = 1 - (staticdata.distance or 0) -- recalculate direction - local next_dir,_ = mod:get_rail_direction(staticdata.connected_at, next_dir, nil, nil, staticdata.railtype) + local next_dir,_ = mod:get_rail_direction(staticdata.connected_at, next_dir) staticdata.dir = next_dir end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index bccbb5d83..b7b389102 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -7,8 +7,8 @@ local S = minetest.get_translator(modname) local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", "Minecart Debug") local friction = mcl_minecarts.FRICTION local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH -DEBUG = true -mcl_debug = function(msg) print(msg) end +--DEBUG = false +--mcl_debug = function(msg) print(msg) end -- Imports local train_length = mod.train_length @@ -30,13 +30,9 @@ end mod.detach_minecart = detach_minecart local function try_detach_minecart(staticdata) - if not staticdata then return end - - -- Don't try to detach if alread detached - if not staticdata.connected_at then return end - - local node = minetest.get_node(staticdata.connected_at) - if minetest.get_item_group(node.name, "rail") == 0 then + if not staticdata or not staticdata.connected_at then return end + if not mod:is_rail(staticdata.connected_at) then + print("Detaching minecart"..tostring(staticdata.uuid)) detach_minecart(staticdata) end end @@ -77,10 +73,10 @@ local function handle_cart_enter_exit(staticdata, pos, next_dir, event) -- Handle cart-specific behaviors if luaentity then local hook = luaentity["_mcl_minecarts_"..event] + if hook then hook(self, pos) end else - minetest.log("warning", "TODO: chanve _mcl_minecarts_"..event.." calling so it is not dependent on the existence of a luaentity") + --minetest.log("warning", "TODO: change _mcl_minecarts_"..event.." calling so it is not dependent on the existence of a luaentity") end - if hook then hook(self, pos) end end local function set_metadata_cart_status(pos, uuid, state) local meta = minetest.get_meta(pos) @@ -117,6 +113,8 @@ local function handle_cart_node_watches(staticdata, dtime) end local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) + if not cart1_staticdata then return end + -- Look ahead one block local pos = vector.add(prev_pos, next_dir) @@ -387,7 +385,7 @@ local function do_movement_step(staticdata, dtime) end -- Handle cart collisions - handle_cart_collision(self, pos, next_dir) + handle_cart_collision(staticdata, pos, next_dir) -- Leave the old node handle_cart_leave(staticdata, old_pos, next_dir ) diff --git a/mods/ENTITIES/mcl_minecarts/storage.lua b/mods/ENTITIES/mcl_minecarts/storage.lua index b31433c1d..c68942778 100644 --- a/mods/ENTITIES/mcl_minecarts/storage.lua +++ b/mods/ENTITIES/mcl_minecarts/storage.lua @@ -54,6 +54,10 @@ function mod.destroy_cart_data(uuid) cart_data_fail_cache[uuid] = true end +function mod.carts() + return pairs(cart_data) +end + function mod.find_carts_by_block_map(block_map) local cart_list = {} for _,data in pairs(cart_data) do @@ -68,3 +72,8 @@ function mod.find_carts_by_block_map(block_map) return cart_list end +minetest.register_on_shutdown(function() + for uuid,_ in pairs(cart_data) do + save_cart_data(uuid) + end +end) From c99f972b31e6cbc67f237a882c24759631a96f10 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 9 Apr 2024 22:32:58 +0000 Subject: [PATCH 077/195] Remove memory leak for cart data, check distance to players before respawning distant carts to prevent adding entities that are immediately inactivated --- mods/ENTITIES/mcl_minecarts/carts.lua | 42 ++++++++++++++++----------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index e2e105285..e66504598 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -119,20 +119,15 @@ function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) data = cd end - -- Initialize - if type(data) == "table" then - -- Fix up types - data.dir = vector.new(data.dir) + -- Fix up types + data.dir = vector.new(data.dir) - -- Fix mass - data.mass = data.mass or 1 + -- Fix mass + data.mass = data.mass or 1 - -- Make sure all carts have an ID to isolate them - self._uuid = data.uuid - data.uuid = mcl_util.get_uuid(self.object) - - self._staticdata = data - end + -- Make sure all carts have an ID to isolate them + self._uuid = data.uuid + self._staticdata = data -- Activate cart if on powered activator rail if self.on_activate_by_rail then @@ -311,9 +306,6 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) end local entity_id = entity_mapping[itemstack:get_name()] - local cart = minetest.add_entity(spawn_pos, entity_id) - - cart:set_yaw(minetest.dir_to_yaw(cart_dir)) -- Setup cart data local uuid = mcl_util.get_uuid(cart) @@ -323,6 +315,12 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) update_cart_data(data) save_cart_data(uuid) + -- Create the entity with the staticdata already setup + local sd = minetest.serialize({ uuid=uuid, seq=1 }) + local cart = minetest.add_entity(spawn_pos, entity_id, sd) + + cart:set_yaw(minetest.dir_to_yaw(cart_dir)) + -- Update static data local le = cart:get_luaentity() if le then @@ -465,14 +463,24 @@ end local function respawn_cart(cart) local cart_type = cart.cart_type or "mcl_minecarts:minecart" local pos = mod.get_cart_position(cart) - print("Respawning cart #"..cart.uuid.." at "..tostring(pos)) + + local players = minetest.get_connected_players() + local distance = nil + for _,player in pairs(players) do + local d = vector.distance(player:get_pos(), pos) + if not distance or d < distance then distance = d end + end + if not distance or distance > 115 then return end + + print("Respawning cart #"..cart.uuid.." at "..tostring(pos)..",distance="..distance) -- Update sequence so that old cart entities get removed cart.seq = (cart.seq or 1) + 1 save_cart_data(cart.uuid) -- Create the new entity - local entity = minetest.add_entity(pos, cart_type) + local sd = minetest.serialize({ uuid=cart.uuid, seq=cart.seq }) + local entity = minetest.add_entity(pos, cart_type, sd) local le = entity:get_luaentity() le._seq = cart.seq le._uuid = cart.uuid From 135eea4de465e6522ca06036a46eba0fbe5b485e Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 9 Apr 2024 23:09:10 +0000 Subject: [PATCH 078/195] Fix crashes --- mods/CORE/mcl_util/init.lua | 16 ++++++++++------ mods/ENTITIES/mcl_minecarts/carts.lua | 6 +++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 68478fc5a..b8532d5a0 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -837,11 +837,7 @@ end function mcl_util.get_luaentity_from_uuid(uuid) return minetest.luaentities[ mcl_util.get_active_object_id_from_uuid(uuid) ] end -function mcl_util.get_uuid(obj) - local le = obj:get_luaentity() - - if le._uuid then return le._uuid end - +function mcl_util.gen_uuid() -- Generate a random 128-bit ID that can be assumed to be unique -- To have a 1% chance of a collision, there would have to be 1.6x10^76 IDs generated -- https://en.wikipedia.org/wiki/Birthday_problem#Probability_table @@ -849,7 +845,15 @@ function mcl_util.get_uuid(obj) for i = 1,16 do u[#u + 1] = string.format("%02X",math.random(1,255)) end - le._uuid = table.concat(u) + return table.concat(u) +end +function mcl_util.assign_uuid(obj) + assert(obj) + + local le = obj:get_luaentity() + if le._uuid then return le._uuid end + + le._uuid = mcl_util.gen_uuid() -- Update the cache with this new id aoid = mcl_util.get_active_object_id(obj) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index e66504598..78cc552f7 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -107,7 +107,7 @@ function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) -- Transfer older data local data = minetest.deserialize(staticdata) or {} if not data.uuid then - data.uuid = mcl_util.get_uuid(self.object) + data.uuid = mcl_util.assign_uuid(self.object) end self._seq = data.seq or 1 @@ -202,7 +202,7 @@ function DEFAULT_CART_DEF:on_step(dtime) -- Regen local hp = self.object:get_hp() local time_now = minetest.get_gametime() - if hp < MINECART_MAX_HP and staticdata.last_regen <= time_now - 1 then + if hp < MINECART_MAX_HP and (staticdata.last_regen or 0) <= time_now - 1 then staticdata.last_regen = time_now hp = hp + 1 self.object:set_hp(hp) @@ -308,7 +308,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) local entity_id = entity_mapping[itemstack:get_name()] -- Setup cart data - local uuid = mcl_util.get_uuid(cart) + local uuid = mcl_util.gen_uuid() data = make_staticdata( nil, railpos, cart_dir ) data.uuid = uuid data.cart_type = entity_id From 1eda16a1a1fca1b5014a1ef65c3ceefcc89318a1 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 9 Apr 2024 23:27:49 +0000 Subject: [PATCH 079/195] Tune respawn distance limit --- mods/CORE/mcl_util/init.lua | 6 +++--- mods/ENTITIES/mcl_minecarts/carts.lua | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index b8532d5a0..ec58fc566 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -851,9 +851,9 @@ function mcl_util.assign_uuid(obj) assert(obj) local le = obj:get_luaentity() - if le._uuid then return le._uuid end - - le._uuid = mcl_util.gen_uuid() + if not le._uuid then + le._uuid = mcl_util.gen_uuid() + end -- Update the cache with this new id aoid = mcl_util.get_active_object_id(obj) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 78cc552f7..00ee39356 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -470,21 +470,20 @@ local function respawn_cart(cart) local d = vector.distance(player:get_pos(), pos) if not distance or d < distance then distance = d end end - if not distance or distance > 115 then return end + if not distance or distance > 90 then return end - print("Respawning cart #"..cart.uuid.." at "..tostring(pos)..",distance="..distance) + print("Respawning cart #"..cart.uuid.." at "..tostring(pos)..",distance="..distance..",node="..minetest.get_node(pos).name) -- Update sequence so that old cart entities get removed cart.seq = (cart.seq or 1) + 1 save_cart_data(cart.uuid) - -- Create the new entity + -- Create the new entity and refresh caches local sd = minetest.serialize({ uuid=cart.uuid, seq=cart.seq }) local entity = minetest.add_entity(pos, cart_type, sd) local le = entity:get_luaentity() - le._seq = cart.seq - le._uuid = cart.uuid le._staticdata = cart + mcl_util.assign_uuid(entity) -- We intentionally don't call the normal hooks because this minecart was already there end From 74a14a5a74b342b6e4ec1ac26bc18dc66bf00a5d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 10 Apr 2024 07:40:25 +0000 Subject: [PATCH 080/195] Implement movement thru tee rails --- mods/ENTITIES/mcl_minecarts/movement.lua | 2 +- mods/ENTITIES/mcl_minecarts/rails.lua | 65 ++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index b7b389102..61a3b9629 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -32,7 +32,7 @@ mod.detach_minecart = detach_minecart local function try_detach_minecart(staticdata) if not staticdata or not staticdata.connected_at then return end if not mod:is_rail(staticdata.connected_at) then - print("Detaching minecart"..tostring(staticdata.uuid)) + print("Detaching minecart #"..tostring(staticdata.uuid)) detach_minecart(staticdata) end end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index b3d304b9b..835b30b06 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -89,11 +89,65 @@ local function rail_dir_curve(pos, dir, node) if vector.equals(dir, east) then return east end end end -local function rail_dir_tee(pos, dir, node) + + +local function rail_dir_tee_off(pos, dir, node) dir = vector.new(dir.x, 0, dir.z) - minetest.log("warning","TODO: implement rail_dir_tee()") - return north + if node.param2 == 0 then -- north + -- South and East + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return east end + if vector.equals(dir, west) then return south end + if vector.equals(dir, east) then return east end + elseif node.param2 == 1 then -- east + -- South and West + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return west end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return south end + elseif node.param2 == 2 then + -- North and West + if vector.equals(dir, south) then return west end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return north end + elseif node.param2 == 3 then + -- North and East + if vector.equals(dir, south) then return east end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return north end + if vector.equals(dir, east) then return east end + end +end +local function rail_dir_tee_on(pos, dir, node) + dir = vector.new(dir.x, 0, dir.z) + + if node.param2 == 0 then -- north + -- South and East + if vector.equals(dir, south) then return east end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return north end + if vector.equals(dir, east) then return east end + elseif node.param2 == 1 then -- east + -- South and West + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return east end + if vector.equals(dir, west) then return south end + if vector.equals(dir, east) then return east end + elseif node.param2 == 2 then + -- North and West + if vector.equals(dir, south) then return south end + if vector.equals(dir, north) then return west end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return south end + elseif node.param2 == 3 then + -- North and East + if vector.equals(dir, south) then return west end + if vector.equals(dir, north) then return north end + if vector.equals(dir, west) then return west end + if vector.equals(dir, east) then return north end + end end local function rail_dir_cross(pos, dir, node) dir = vector.new(dir.x, 0, dir.z) @@ -292,13 +346,14 @@ function mod.register_curves_rail(base_name, tiles, def) } }, _mcl_minecarts = { - railtype = "corner", + get_next_dir = rail_dir_tee_off, + railtype = "tee", }, })) mod.register_rail(base_name.."_tee_on", table_merge(table.copy(base_def),{ tiles = { tiles[4] }, _mcl_minecarts = { - get_next_dir = rail_dir_tee, + get_next_dir = rail_dir_tee_on, railtype = "tee", }, mesecons = { From 867fc20782ad24a7b3919da27043d2447d567c9d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 10 Apr 2024 08:02:21 +0000 Subject: [PATCH 081/195] Fix typo in rail replacement mapping, fix several crashes --- mods/ENTITIES/mcl_minecarts/movement.lua | 6 +++--- mods/ENTITIES/mcl_minecarts/rails.lua | 2 +- mods/ENTITIES/mcl_minecarts/train.lua | 5 +---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 61a3b9629..13f881187 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -154,8 +154,8 @@ local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) local m2 = cart2_staticdata.mass --print("u1="..tostring(u1)..",u2="..tostring(u2)) - if u2 == 0 and u1 < 4 and train_length(cart1) < MAX_TRAIN_LENGTH then - link_cart_ahead(cart1, {_staticdata=cart2_staticdata}) + if u2 == 0 and u1 < 4 and train_length(cart1_staticdata) < MAX_TRAIN_LENGTH then + link_cart_ahead(cart1_staticdata, cart2_staticdata) cart2_staticdata.dir = mcl_minecarts:get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) cart2_staticdata.velocity = cart1_staticdata.velocity return @@ -268,7 +268,7 @@ end local function reverse_direction(staticdata) if staticdata.behind or staticdata.ahead then - reverse_train(self) + reverse_train(staticdata) return end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 835b30b06..22b26cd8e 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -636,7 +636,7 @@ minetest.register_lbm({ local STRAIGHT_RAILS_MAP ={ ["mcl_minecarts:golden_rail"] = "mcl_minecarts:golden_rail_v2", ["mcl_minecarts:golden_rail_on"] = "mcl_minecarts:golden_rail_v2_on", - ["mcl_minecarts:activator_rail"] = "mcl_minecarts_activator_rail_v2", + ["mcl_minecarts:activator_rail"] = "mcl_minecarts:activator_rail_v2", ["mcl_minecarts:activator_rail_on"] = "mcl_minecarts:activator_rail_v2_on", ["mcl_minecarts:detector_rail"] = "mcl_minecarts:detector_rail_v2", ["mcl_minecarts:detector_rail_on"] = "mcl_minecarts:detector_rail_v2_on", diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index d60678a09..279c21f0e 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -139,10 +139,7 @@ function mod.update_train(staticdata) end end -function mod.link_cart_ahead(cart, cart_ahead) - local staticdata = cart._staticdata - local ca_staticdata = cart_ahead._staticdata - +function mod.link_cart_ahead(staticdata, ca_staticdata) minetest.log("action","Linking cart #"..staticdata.uuid.." to cart #"..ca_staticdata.uuid) staticdata.ahead = ca_staticdata.uuid From 48b1a4d7e5ab4fa0aeb567725efbad5021ea0562 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 10 Apr 2024 09:03:59 +0000 Subject: [PATCH 082/195] Fix cart controls, cart pushing --- mods/ENTITIES/mcl_minecarts/carts.lua | 7 +++++-- mods/ENTITIES/mcl_minecarts/movement.lua | 13 +++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 00ee39356..dce5124d3 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -229,8 +229,11 @@ function DEFAULT_CART_DEF:on_step(dtime) end -- Experimental controls - self._go_forward = ctrl.up - self._brake = ctrl.down + local now_time = minetest.get_gametime() + local controls = {} + if ctrl.up then controls.forward = now_time end + if ctrl.down then controls.brake = now_time end + staticdata.controls = controls end -- Give achievement when player reached a distance of 1000 nodes from the start position diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 13f881187..a0b053a2a 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -194,7 +194,7 @@ local function vector_away_from_players(cart, staticdata) for _,obj in pairs(minetest.get_objects_inside_radius(cart_pos, 1.1)) do if player_repel(obj) then - return obj:get_pos() - self.object:get_pos() + return obj:get_pos() - cart_pos end end @@ -202,7 +202,7 @@ local function vector_away_from_players(cart, staticdata) end local function direction_away_from_players(staticdata) - local diff = vector_away_from_players(staticdata) + local diff = vector_away_from_players(nil,staticdata) if not diff then return 0 end local length = vector.distance(vector.new(0,0,0),diff) @@ -242,10 +242,11 @@ local function calculate_acceleration(staticdata) local max_vel = mcl_minecarts.speed_max local ctrl = staticdata.controls or {} + local time_active = minetest.get_gametime() - 0.25 - if ctrl.go_forward then + if (ctrl.forward or 0) > time_active then acceleration = 4 - elseif ctrl.brake then + elseif (ctrl.brake or 0) > time_active then acceleration = -1.5 elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then acceleration = 0.6 @@ -285,11 +286,11 @@ local function do_movement_step(staticdata, dtime) local v_0 = staticdata.velocity -- Repel minecarts - local away = direction_away_from_players(nil, staticdata) + local away = direction_away_from_players(staticdata) if away > 0 then v_0 = away elseif away < 0 then - reverse_direction(self, staticdata) + reverse_direction(staticdata) v_0 = -away end From dba86a8d60d39ea68cabac79ece2ec4ff7a7aac8 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 10 Apr 2024 22:22:17 +0000 Subject: [PATCH 083/195] Correct crashes/item duplication with dropping carts, start API documentation --- mods/ENTITIES/mcl_minecarts/API.md | 44 +++++++++++++++ mods/ENTITIES/mcl_minecarts/carts.lua | 69 +++++++++++++++--------- mods/ENTITIES/mcl_minecarts/movement.lua | 4 +- 3 files changed, 89 insertions(+), 28 deletions(-) create mode 100644 mods/ENTITIES/mcl_minecarts/API.md diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md new file mode 100644 index 000000000..88794b1ba --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -0,0 +1,44 @@ +== Cart-Node interactions + +As the cart moves thru the environment, it can interact with the surrounding blocks +thru a number of handlers in the block definitions. All these handlers are defined +as: + +`function(node_position, cart_luaentity, cart_direction, cart_position)` + +Arguments: +`node_position` - position of the node the cart is interacting with +`cart_luaentity` - The luaentity of the cart that is entering this block. Will + be nil for minecarts moving thru unloaded blocks +`cart_direction` - The direction the cart is moving +`cart_position` - The location of the cart +`cart_data` - Information about the cart. This will always be defined. + +There are several variants of this handler: +`_mcl_minecarts_on_enter` - The cart enters this block +`_mcl_minecarts_on_enter_below` - The cart enters above this block +`_mcl_minecarts_on_enter_above` - The cart enters below this block +`_mcl_minecarts_on_enter_side` - The cart enters beside this block + +Mods can also define global handlers that are called for every node. These +handlers are defined as: + +`function(node_position, cart_luaentity, cart_direction, node_definition, cart_data)` + +Arguments: +`node_position` - position of the node the cart is interacting with +`cart_luaentity` - The luaentity of the cart that is entering this block. Will + be nil for minecarts moving thru unloaded blocks +`cart_direction` - The direction the cart is moving +`cart_position` - The location of the cart +`cart_data` - Information about the cart. This will always be defined. +`node_definition` - The definition of the node at `node_position` + +The available hooks are: +`_mcl_minecarts.on_enter` - The cart enters this block +`_mcl_minecarts.on_enter_below` - The cart enters above this block +`_mcl_minecarts.on_enter_above` - The cart enters below this block +`_mcl_minecarts.on_enter_side` - The cart enters beside this block + +Only a single function can be installed in each of these handlers. Before installing, +preserve the existing handler and call it from inside your handler if not `nil`. diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index dce5124d3..846ba7058 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -249,18 +249,10 @@ function DEFAULT_CART_DEF:on_step(dtime) mod.update_cart_orientation(self) end -function DEFAULT_CART_DEF:on_death(killer) - local staticdata = self._staticdata +local function kill_cart(staticdata) + local pos minetest.log("action", "cart #"..staticdata.uuid.." was killed") - detach_driver(self) - - -- Detach passenger - if self._passenger then - local mob = self._passenger.object - mob:set_detach() - end - -- Leave nodes if staticdata.attached_at then handle_cart_leave(self, staticdata.attached_at, staticdata.dir ) @@ -268,23 +260,42 @@ function DEFAULT_CART_DEF:on_death(killer) mcl_log("TODO: handle detatched minecart death") end + -- Handle entity-related items + local le = mcl_util.get_luaentity_from_uuid(staticdata.uuid) + if le then + pos = le.object:get_pos() + + detach_driver(le) + + -- Detach passenger + if le._passenger then + local mob = le._passenger.object + mob:set_detach() + end + else + pos = mod.get_cart_position(staticdata) + end + + -- Drop items + if not staticdata.dropped then + local entity_def = minetest.registered_entities[staticdata.cart_type] + if entity_def then + local drop = entity_def.drop + for d=1, #drop do + minetest.add_item(pos, drop[d]) + end + + -- Prevent item duplication + staticdata.dropped = true + end + end + -- Remove data destroy_cart_data(staticdata.uuid) - -- Drop items - local drop = self.drop - if not killer or not minetest.is_creative_enabled(killer:get_player_name()) then - for d=1, #drop do - minetest.add_item(self.object:get_pos(), drop[d]) - end - elseif killer and killer:is_player() then - local inv = killer:get_inventory() - for d=1, #drop do - if not inv:contains_item("main", drop[d]) then - inv:add_item("main", drop[d]) - end - end - end +end +function DEFAULT_CART_DEF:on_death(killer) + kill_cart(self._staticdata) end -- Place a minecart at pointed_thing @@ -558,8 +569,14 @@ minetest.register_globalstep(function(dtime) local r = 0.6 for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then - self:on_death() - self.object:remove() + kill_cart(staticdata) + local le = mcl_util.get_luaentity_from_uuid(staticdata.uuid) + if le then + le:on_death() + le.object:remove() + else + kill_cart(staticdata) + end return end end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index a0b053a2a..31e95a4e9 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -62,11 +62,11 @@ local function handle_cart_enter_exit(staticdata, pos, next_dir, event) -- node-specific hook local hook_name = "_mcl_minecarts_"..event..check[4] local hook = node_def[hook_name] - if hook then hook(check_pos, luaentity, next_dir, pos) end + if hook then hook(check_pos, luaentity, next_dir, pos, staticdata) end -- global minecart hook hook = mcl_minecarts[event..check[4]] - if hook then hook(check_pos, luaentity, next_dir, node_def) end + if hook then hook(check_pos, luaentity, next_dir, pos, staticdata, node_def) end end end From 19f21db8c9a1816c29bd63861ab860b5dac83389 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 10 Apr 2024 22:48:24 +0000 Subject: [PATCH 084/195] Change document formatting, finally move cactus cart dropping to node definition for mcl_core:cactus --- mods/ENTITIES/mcl_minecarts/API.md | 32 ++++++++++++------------ mods/ENTITIES/mcl_minecarts/carts.lua | 28 +++++---------------- mods/ITEMS/mcl_core/nodes_cactuscane.lua | 7 +++++- 3 files changed, 28 insertions(+), 39 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md index 88794b1ba..b43af1af7 100644 --- a/mods/ENTITIES/mcl_minecarts/API.md +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -1,4 +1,4 @@ -== Cart-Node interactions +## Cart-Node interactions As the cart moves thru the environment, it can interact with the surrounding blocks thru a number of handlers in the block definitions. All these handlers are defined @@ -7,18 +7,18 @@ as: `function(node_position, cart_luaentity, cart_direction, cart_position)` Arguments: -`node_position` - position of the node the cart is interacting with -`cart_luaentity` - The luaentity of the cart that is entering this block. Will +- `node_position` - position of the node the cart is interacting with +- `cart_luaentity` - The luaentity of the cart that is entering this block. Will be nil for minecarts moving thru unloaded blocks -`cart_direction` - The direction the cart is moving -`cart_position` - The location of the cart -`cart_data` - Information about the cart. This will always be defined. +- `cart_direction` - The direction the cart is moving +- `cart_position` - The location of the cart +- `cart_data` - Information about the cart. This will always be defined. There are several variants of this handler: -`_mcl_minecarts_on_enter` - The cart enters this block -`_mcl_minecarts_on_enter_below` - The cart enters above this block -`_mcl_minecarts_on_enter_above` - The cart enters below this block -`_mcl_minecarts_on_enter_side` - The cart enters beside this block +- `_mcl_minecarts_on_enter` - The cart enters this block +- `_mcl_minecarts_on_enter_below` - The cart enters above this block +- `_mcl_minecarts_on_enter_above` - The cart enters below this block +- `_mcl_minecarts_on_enter_side` - The cart enters beside this block Mods can also define global handlers that are called for every node. These handlers are defined as: @@ -26,13 +26,13 @@ handlers are defined as: `function(node_position, cart_luaentity, cart_direction, node_definition, cart_data)` Arguments: -`node_position` - position of the node the cart is interacting with -`cart_luaentity` - The luaentity of the cart that is entering this block. Will +- `node_position` - position of the node the cart is interacting with +- `cart_luaentity` - The luaentity of the cart that is entering this block. Will be nil for minecarts moving thru unloaded blocks -`cart_direction` - The direction the cart is moving -`cart_position` - The location of the cart -`cart_data` - Information about the cart. This will always be defined. -`node_definition` - The definition of the node at `node_position` +- `cart_direction` - The direction the cart is moving +- `cart_position` - The location of the cart +- `cart_data` - Information about the cart. This will always be defined. +- `node_definition` - The definition of the node at `node_position` The available hooks are: `_mcl_minecarts.on_enter` - The cart enters this block diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 846ba7058..6371c860a 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -249,7 +249,7 @@ function DEFAULT_CART_DEF:on_step(dtime) mod.update_cart_orientation(self) end -local function kill_cart(staticdata) +function mod.kill_cart(staticdata) local pos minetest.log("action", "cart #"..staticdata.uuid.." was killed") @@ -272,6 +272,9 @@ local function kill_cart(staticdata) local mob = le._passenger.object mob:set_detach() end + + -- Remove the entity + le.object:remove() else pos = mod.get_cart_position(staticdata) end @@ -292,8 +295,9 @@ local function kill_cart(staticdata) -- Remove data destroy_cart_data(staticdata.uuid) - end +local kill_cart = mod.kill_cart + function DEFAULT_CART_DEF:on_death(killer) kill_cart(self._staticdata) end @@ -561,26 +565,6 @@ minetest.register_globalstep(function(dtime) --- Non-entity code if staticdata.connected_at then do_movement(staticdata, dtime) - - -- TODO: move this into mcl_core:cactus _mcl_minecarts_on_enter_side - -- Drop minecart if it collides with a cactus node - local pos = mod.get_cart_position(staticdata) - if pos then - local r = 0.6 - for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do - if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then - kill_cart(staticdata) - local le = mcl_util.get_luaentity_from_uuid(staticdata.uuid) - if le then - le:on_death() - le.object:remove() - else - kill_cart(staticdata) - end - return - end - end - end end end end) diff --git a/mods/ITEMS/mcl_core/nodes_cactuscane.lua b/mods/ITEMS/mcl_core/nodes_cactuscane.lua index 805385124..5e37a92c4 100644 --- a/mods/ITEMS/mcl_core/nodes_cactuscane.lua +++ b/mods/ITEMS/mcl_core/nodes_cactuscane.lua @@ -48,6 +48,11 @@ minetest.register_node("mcl_core:cactus", { end), _mcl_blast_resistance = 0.4, _mcl_hardness = 0.4, + _mcl_minecarts_on_enter_side = function(pos, _, _, _, cart_data) + if mcl_minecarts then + mcl_minecarts.kill_cart(cart_data) + end + end, }) minetest.register_node("mcl_core:reeds", { @@ -135,4 +140,4 @@ minetest.register_node("mcl_core:reeds", { end, _mcl_blast_resistance = 0, _mcl_hardness = 0, -}) \ No newline at end of file +}) From c44d291feec24d2527804ef5f3ead4c92869d8bc Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 10 Apr 2024 23:30:03 +0000 Subject: [PATCH 085/195] Continue writing API documentation, update call signatures for a couple of API functions --- mods/ENTITIES/mcl_minecarts/API.md | 92 ++++++++++++++++++++++- mods/ENTITIES/mcl_minecarts/functions.lua | 4 +- 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md index b43af1af7..16db4878d 100644 --- a/mods/ENTITIES/mcl_minecarts/API.md +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -1,3 +1,87 @@ +# Table of Contents +1. Useful Constants +2. Rail +3. [Cart functions](#cart-functions) +4. [Cart-Node Interactions](#cart-node-iteractions) + +## Useful Constants + +`mcl_minecarts.north` +`mcl_minecarts.south` +`mcl_minecarts.east` +`mcl_minecarts.west` + +Human-readable names for the cardinal directions. + +## Rail + +### Constants + +`mcl_minecarts.HORIZONTAL_CURVES_RULES` +`mcl_minecarts.HORIZONTAL_STANDARD_RULES` + +Rail connection rules. Each rule is an table with the following indexes: + +1. `node_name_suffix` - The suffix added to a node's `_mcl_minecarts.base_name` to + get the name of the node to use for this connection. +2. `param2_value` - The value of the node's param2. Used to specify rotation. + +and the following named options: + +- `mask` - Directional connections mask +- `score` - priority of the rule. If more than one rule matches, the one with the + highest store is selected. +- `can_slope` - true if the result of this rule can be converted into a slope. + +### Functions + +`mcl_minecarts.get_rail_connections(node_position, options)` + +Calculate the rail adjacency information for rail placement. Arguments are: + +- `node_position` - the location of the node to calculate adjacency for. +- `options` - A table containing any of these options: + - `legacy`- if true, don't check that a connection proceeds out in a direction + a cart can travel. Used for converting legacy rail to newer equivalents. + - `ignore_neightbor_connections` - if true, don't check that a cart could leave + the neighboring node from this direction. + +`mcl_minecarts.update_rail_connections(node_position, options)` + +Converts the rail at `node_position`, if possible, another variant (curve, etc.) +and rotates the node as needed so that rails connect together. `options` is +passed thru to `mcl_minecarts.get_rail_connections()` + +`mcl_minecarts:get_rail_direction(rail_position, cart_direction)` + +Returns the next direction a cart traveling in the direction specified in `cart_direction` +will travel from the rail located at `rail_position`. + +## Cart functions + +`mcl_minecarts.detach_minecart(cart_data)` + +This detaches a minecart from any rail it is attached to and makes it start moving +as an entity affected by gravity. It will keep moving in the same direction and +at the same speed it was moving at before it detaches. + +`mcl_minecarts.get_cart_position(cart_data)` + +Compute the location of a minecart from its cart data. This works even when the entity +is unloaded. + +`mcl_minecarts.reverse_cart_direction(cart_data)` + +Force a minecart to start moving in the opposite direction of its current direction. + +`mcl_minecarts.snap_direction(direction_vector)` + +Returns a valid cart movement direction that has the smallest angle between it and `direction_vector`. + +`mcl_minecarts:update_cart_orientation(cart)` + +Updates the rotation of a cart entity to match the cart's data. + ## Cart-Node interactions As the cart moves thru the environment, it can interact with the surrounding blocks @@ -35,10 +119,10 @@ Arguments: - `node_definition` - The definition of the node at `node_position` The available hooks are: -`_mcl_minecarts.on_enter` - The cart enters this block -`_mcl_minecarts.on_enter_below` - The cart enters above this block -`_mcl_minecarts.on_enter_above` - The cart enters below this block -`_mcl_minecarts.on_enter_side` - The cart enters beside this block +- `_mcl_minecarts.on_enter` - The cart enters this block +- `_mcl_minecarts.on_enter_below` - The cart enters above this block +- `_mcl_minecarts.on_enter_above` - The cart enters below this block +- `_mcl_minecarts.on_enter_side` - The cart enters beside this block Only a single function can be installed in each of these handlers. Before installing, preserve the existing handler and call it from inside your handler if not `nil`. diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 40d5cd2d3..7fdc2e195 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -285,7 +285,7 @@ local function is_ahead_slope(pos, dir) local node_name = force_get_node(below).name return minetest.get_item_group(node_name, "rail_slope") ~= 0 end -function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) +function mcl_minecarts:get_rail_direction(pos_, dir) local pos = vector.round(pos_) -- Handle new track types that have track-specific direction handler @@ -312,7 +312,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype) return dir end -function mcl_minecarts:update_cart_orientation() +function mcl_minecarts.update_cart_orientation(self) local staticdata = self._staticdata -- constants From 7c1b7801ceab899f84fb63c9b6fd7f0efd914a7c Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 00:04:59 +0000 Subject: [PATCH 086/195] Nearly finish API documentation, create mcl_minecarts.add_blocks_to_map() --- mods/ENTITIES/mcl_minecarts/API.md | 115 +++++++++++++++++++++++- mods/ENTITIES/mcl_minecarts/carts.lua | 14 ++- mods/ENTITIES/mcl_minecarts/storage.lua | 12 +++ 3 files changed, 128 insertions(+), 13 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md index 16db4878d..52e639231 100644 --- a/mods/ENTITIES/mcl_minecarts/API.md +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -1,6 +1,6 @@ # Table of Contents -1. Useful Constants -2. Rail +1. [Useful Constants](#useful-constants) +2. [Rail](#rail) 3. [Cart functions](#cart-functions) 4. [Cart-Node Interactions](#cart-node-iteractions) @@ -33,6 +33,11 @@ and the following named options: highest store is selected. - `can_slope` - true if the result of this rule can be converted into a slope. +`mcl_minecarts.RAIL_GROUPS.STANDARD` +`mcl_minecarts.RAIL_GROUPS.CURVES` + +These constants are used to specify a rail node's `group.rail` value. + ### Functions `mcl_minecarts.get_rail_connections(node_position, options)` @@ -46,6 +51,19 @@ Calculate the rail adjacency information for rail placement. Arguments are: - `ignore_neightbor_connections` - if true, don't check that a cart could leave the neighboring node from this direction. +`mcl_minecarts.register_rail(itemstring, node_definition)` + +Registers a rail with a few sensible defaults and if a craft recipe was specified, +register that as well. + +`mcl_minecarts.register_straight_rail(base_name, tiles, node_definition)` + +Registers a rail with only straight and sloped variants. + +`mcl_minecarts.register_curves_rail(base_name, tiles, node_definition)` + +Registers a rail with straight, sloped, curved, tee and cross variants. + `mcl_minecarts.update_rail_connections(node_position, options)` Converts the rail at `node_position`, if possible, another variant (curve, etc.) @@ -57,7 +75,25 @@ passed thru to `mcl_minecarts.get_rail_connections()` Returns the next direction a cart traveling in the direction specified in `cart_direction` will travel from the rail located at `rail_position`. -## Cart functions +### Node Definition Options + +`_mcl_minecarts.railtype` + +This declares the variant type of the rail. This will be one of the following: + +- "straight" - two connections opposite each other and no vertical change. +- "sloped" - two connections opposite each other with one of these connections + one block higher. +- "corner" - two connections at 90 degrees from each other. +- "tee" - three connections +- "cross" - four connections allowing only straight-thru movement + +#### Hooks +`_mcl_minecarts.get_next_dir = function(node_position, current_direction, node)` + +Called to get the next direction a cart will travel after passing thru this node. + +## Cart Functions `mcl_minecarts.detach_minecart(cart_data)` @@ -70,6 +106,10 @@ at the same speed it was moving at before it detaches. Compute the location of a minecart from its cart data. This works even when the entity is unloaded. +`mcl_minecarts.kill_cart(cart_data)` + +Kills a cart and drops it as an item, even if the cart entity is unloaded. + `mcl_minecarts.reverse_cart_direction(cart_data)` Force a minecart to start moving in the opposite direction of its current direction. @@ -82,7 +122,36 @@ Returns a valid cart movement direction that has the smallest angle between it a Updates the rotation of a cart entity to match the cart's data. -## Cart-Node interactions +## Cart Data Functions + +`mcl_minecarts.destroy_cart_data(uuid)` + +Destroys the data for the cart with the identitfier in `uuid`. + +`mcl_minecarts.find_carts_by_block_map(block_map)` + +Returns a list of cart data for carts located in the blocks specified in `block_map`. Used +to respawn carts entering areas around players. + +`mcl_minecarts.add_blocks_to_map(block_map, min_pos, max_pos)` + +Add blocks that fully contain `min_pos` and `max_pos` to `block_map` for use by + `mcl_minecarts.find_cart_by_block_map`. + +`mcl_minecarts.get_cart_data(uuid)` + +Loads the data for the cart with the identitfier in `uuid`. + +`mcl_minecarts.save_cart_data(uuid)` + +Saves the data for the cart with the identifier in `uuid`. + +`mcl_minecart.update_cart_data(data)` + +Replaces the cart data for the cart with the identifier in `data.uuid`, then saves +the data. + +## Cart-Node Interactions As the cart moves thru the environment, it can interact with the surrounding blocks thru a number of handlers in the block definitions. All these handlers are defined @@ -126,3 +195,41 @@ The available hooks are: Only a single function can be installed in each of these handlers. Before installing, preserve the existing handler and call it from inside your handler if not `nil`. + +## Train Functions + +`mcl_minecarts.break_train_at(cart_data)` + +Splits a train apart at the specified cart. + +`mcl_minecarts.distance_between_cars(cart1_data, cart2_data)` + +Returns the distance between two carts even if both entities are unloaded, or nil if either +cart is not on a rail. + +`mcl_minecarts.is_in_same_train(cart1_data, cart2_data)` + +Returns true if cart1 and cart2 are a part of the same train and false otherwise. + +`mcl_minecarts.link_cart_ahead(cart_data, cart_ahead_data)` + +Given two carts, link them together into a train, with the second cart ahead of the first. + +`mcl_minecarts.train_cars(cart_data)` + +Use to iterate over all carts in a train. Expected usage: + +`for cart in mcl_minecarts.train_cars(cart) do --[[ code ]] end` + +`mcl_minecarts.reverse_train(cart)` + +Make all carts in a train reverse and start moving in the opposite direction. + +`mcl_minecarts.train_length(cart_data)` + +Compute the current length of the train containing the cart whose data is `cart_data`. + +`mcl_minecarts.update_train(cart_data)` + +When provided with the rear-most cart of a tain, update speeds of all carts in the train +so that it holds together and moves as a unit. diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 6371c860a..7080a5dea 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -513,15 +513,11 @@ local function try_respawn_carts() local players = minetest.get_connected_players() for _,player in pairs(players) do local pos = player:get_pos() - local min = vector.floor(vector.divide(vector.offset(pos,-CART_BLOCK_SIZE,-CART_BLOCK_SIZE,-CART_BLOCK_SIZE), CART_BLOCK_SIZE)) - local max = vector.floor(vector.divide(vector.offset(pos, CART_BLOCK_SIZE, CART_BLOCK_SIZE, CART_BLOCK_SIZE), CART_BLOCK_SIZE)) + vector.new(1,1,1) - for z = min.z,max.z do - for y = min.y,max.y do - for x = min.x,max.x do - block_map[ vector.to_string(vector.new(x,y,z)) ] = true - end - end - end + mod.add_blocks_to_map( + block_map, + vector.offset(pos,-CART_BLOCK_SIZE,-CART_BLOCK_SIZE,-CART_BLOCK_SIZE), + vector.offset(pos, CART_BLOCK_SIZE, CART_BLOCK_SIZE, CART_BLOCK_SIZE) + ) end -- Find all cart data that are in these blocks diff --git a/mods/ENTITIES/mcl_minecarts/storage.lua b/mods/ENTITIES/mcl_minecarts/storage.lua index c68942778..91926835d 100644 --- a/mods/ENTITIES/mcl_minecarts/storage.lua +++ b/mods/ENTITIES/mcl_minecarts/storage.lua @@ -72,6 +72,18 @@ function mod.find_carts_by_block_map(block_map) return cart_list end +function mod.add_block_map(block_map, min_pos, max_pos) + local min = vector.floor(vector.divide(min_pos), CART_BLOCK_SIZE) + local max = vector.floor(vector.divide(max_pos), CART_BLOCK_SIZE) + vector.new(1,1,1) + for z = min.z,max.z do + for y = min.y,max.y do + for x = min.x,max.x do + block_map[ vector.to_string(vector.new(x,y,z)) ] = true + end + end + end +end + minetest.register_on_shutdown(function() for uuid,_ in pairs(cart_data) do save_cart_data(uuid) From 7dcc1046a6b7fdde5e8479be3e6f0ce04ffa9cca Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 07:08:25 +0000 Subject: [PATCH 087/195] Finish writing API documentation, remove drop_railcarts (replaced by try_detach_minecart), rename constants to ALL CAPS for consistency, change mcl_minecarts. to mod. for API function definitions --- mods/ENTITIES/mcl_minecarts/API.md | 52 ++++++++++++++++++++--- mods/ENTITIES/mcl_minecarts/carts.lua | 18 ++++---- mods/ENTITIES/mcl_minecarts/functions.lua | 2 +- mods/ENTITIES/mcl_minecarts/init.lua | 1 - mods/ENTITIES/mcl_minecarts/movement.lua | 19 +++++---- mods/ENTITIES/mcl_minecarts/rails.lua | 16 ------- 6 files changed, 67 insertions(+), 41 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md index 52e639231..978654a32 100644 --- a/mods/ENTITIES/mcl_minecarts/API.md +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -1,18 +1,44 @@ # Table of Contents 1. [Useful Constants](#useful-constants) 2. [Rail](#rail) -3. [Cart functions](#cart-functions) -4. [Cart-Node Interactions](#cart-node-iteractions) + 1. [Constants](#constants) + 2. [Functions](#functions) + 3. [Node Definition Options](#node-definition-options) +3. [Cart Functions](#cart-functions) +4. [Cart Data Functions](#cart-data-functions) +5. [Cart-Node Interactions](#cart-node-iteractions) +6. [Train Functions](#train-functions) ## Useful Constants -`mcl_minecarts.north` -`mcl_minecarts.south` -`mcl_minecarts.east` -`mcl_minecarts.west` +- `mcl_minecarts.north` +- `mcl_minecarts.south` +- `mcl_minecarts.east` +- `mcl_minecarts.west` Human-readable names for the cardinal directions. +- `mcl_minecarts.SPEED_MAX` + +Maximum speed that minecarts will be accelerated to with powered rails, in blocks per +second. Defined as 10 blocks/second. + +- `mcl_minecarts.CART_BLOCKS_SIZE` + +The size of blocks to use when searching for carts to respawn. Default is 64. + +- `mcl_minecarts.FRICTION` + +Rail friction. Defined as is 0.4 blocks/second^2. + +- `mcl_minecarts.MAX_TRAIN_LENGTH` + +The maximum number of carts that can be in a single train. Defined as 4 carts. + +- `mcl_minecarts.PASSENGER_ATTACH_POSITION` + +Where to attach passengers to the minecarts. + ## Rail ### Constants @@ -110,6 +136,20 @@ is unloaded. Kills a cart and drops it as an item, even if the cart entity is unloaded. +`mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)` + +Places a minecart at the location specified by `pointed_thing` + +`mcl_minecarts.register_minecart(minecart_definition)` + +Registers a minecart. `minecart_definition` defines the entity. All the options supported by +normal minetest entities are supported, with a few additions: + +- `craft` - Crafting recipe for this cart. +- `drop` - List of items to drop when the cart is killed. (required) +- `entity_id` - The entity id of the cart. (required) +- `itemstring` - This is the itemstring to use for this entity. (required) + `mcl_minecarts.reverse_cart_direction(cart_data)` Force a minecart to start moving in the opposite direction of its current direction. diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 7080a5dea..f6c2a1436 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -303,7 +303,7 @@ function DEFAULT_CART_DEF:on_death(killer) end -- Place a minecart at pointed_thing -function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) +function mod.place_minecart(itemstack, pointed_thing, placer) if not pointed_thing.type == "node" then return end @@ -369,7 +369,7 @@ local function dropper_place_minecart(dropitem, pos) local node = minetest.get_node(pos) if minetest.get_item_group(node.name, "rail") == 0 then return false end - mcl_minecarts.place_minecart(dropitem, { + mod.place_minecart(dropitem, { above = pos, under = vector.offset(pos,0,-1,0) }) @@ -397,7 +397,7 @@ local function register_minecart_craftitem(itemstring, def) end end - return mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) + return mod.place_minecart(itemstack, pointed_thing, placer) end, _on_dispense = function(stack, pos, droppos, dropnode, dropdir) -- Place minecart as entity on rail. If there's no rail, just drop it. @@ -405,7 +405,7 @@ local function register_minecart_craftitem(itemstring, def) if minetest.get_item_group(dropnode.name, "rail") ~= 0 then -- FIXME: This places minecarts even if the spot is already occupied local pointed_thing = { under = droppos, above = vector.new( droppos.x, droppos.y+1, droppos.z ) } - placed = mcl_minecarts.place_minecart(stack, pointed_thing) + placed = mod.place_minecart(stack, pointed_thing) end if placed == nil then -- Drop item @@ -438,9 +438,11 @@ Register a minecart * on_activate_by_rail: Called when above activator rail * creative: If false, don't show in Creative Inventory ]] -function mcl_minecarts.register_minecart(def) - assert( def.drop, "def.drop is required parameter" ) - assert( def.itemstring, "def.itemstring is required parameter" ) +function mod.register_minecart(def) + -- Make sure all required parameters are present + for _,name in pairs({"drop","itemstring","entity_id"}) do + assert( def[name], "def."..name..", a required parameter, is missing") + end local entity_id = def.entity_id; def.entity_id = nil local craft = def.craft; def.craft = nil @@ -463,7 +465,7 @@ function mcl_minecarts.register_minecart(def) minetest.register_craft(craft) end end -local register_minecart = mcl_minecarts.register_minecart +local register_minecart = mod.register_minecart dofile(modpath.."/carts/minecart.lua") dofile(modpath.."/carts/with_chest.lua") diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 7fdc2e195..c4694cc06 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -312,7 +312,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir) return dir end -function mcl_minecarts.update_cart_orientation(self) +function mod.update_cart_orientation(self) local staticdata = self._staticdata -- constants diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 6a6f93318..dbc5897a4 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -6,7 +6,6 @@ mcl_minecarts.modpath = modpath -- Constants mod.speed_max = 10 -mod.check_float_time = 15 mod.FRICTION = 0.4 mod.MAX_TRAIN_LENGTH = 4 mod.CART_BLOCK_SIZE = 64 diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 31e95a4e9..3c4ba5e83 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -5,12 +5,13 @@ local S = minetest.get_translator(modname) -- Constants local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", "Minecart Debug") -local friction = mcl_minecarts.FRICTION -local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH --DEBUG = false --mcl_debug = function(msg) print(msg) end -- Imports +local FRICTION = mcl_minecarts.FRICTION +local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH +local SPEED_MAX = mod.SPEED_MAX local train_length = mod.train_length local update_train = mod.update_train local reverse_train = mod.reverse_train @@ -233,13 +234,13 @@ local function calculate_acceleration(staticdata) -- Apply friction if moving if staticdata.velocity > 0 then - acceleration = -friction + acceleration = -FRICTION end local pos = staticdata.connected_at local node_name = minetest.get_node(pos).name local node_def = minetest.registered_nodes[node_name] - local max_vel = mcl_minecarts.speed_max + local max_vel = SPEED_MAX local ctrl = staticdata.controls or {} local time_active = minetest.get_gametime() - 0.25 @@ -259,9 +260,9 @@ local function calculate_acceleration(staticdata) -- Factor in gravity after everything else local gravity_strength = 2.45 --friction * 5 if staticdata.dir.y < 0 then - acceleration = gravity_strength - friction + acceleration = gravity_strength - FRICTION elseif staticdata.dir.y > 0 then - acceleration = -gravity_strength + friction + acceleration = -gravity_strength + FRICTION end return acceleration @@ -330,7 +331,7 @@ local function do_movement_step(staticdata, dtime) end -- Truncate timestep to prevent v_1 from being larger that speed_max - local v_max = mcl_minecarts.speed_max + local v_max = SPEED_MAX if (v_0 ~= v_max) and ( v_0 + a * timestep > v_max) then timestep = ( v_max - v_0 ) / a end @@ -342,7 +343,7 @@ local function do_movement_step(staticdata, dtime) local v_1 = v_0 + a * timestep if v_1 > v_max then v_1 = v_max - elseif v_1 < friction / 5 then + elseif v_1 < FRICTION / 5 then v_1 = 0 end @@ -402,7 +403,7 @@ local function do_movement_step(staticdata, dtime) -- Update cart direction staticdata.dir = next_dir - elseif stops_in_block and v_1 < (friction/5) and a <= 0 then + elseif stops_in_block and v_1 < (FRICTION/5) and a <= 0 then -- Handle direction flip due to gravity if DEBUG then mcl_debug("Gravity flipped direction") end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 22b26cd8e..632e7859e 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -16,21 +16,6 @@ local south = mod.south local east = mod.east local west = mod.west -local function drop_railcarts(pos) - -- Scan for minecarts in this pos and force them to execute their "floating" check. - -- Normally, this will make them drop. - local objs = minetest.get_objects_inside_radius(pos, 1) - for o=1, #objs do - local le = objs[o]:get_luaentity() - if le then - -- All entities in this mod are minecarts, so this works - if string.sub(le.name, 1, 14) == "mcl_minecarts:" then - le._last_float_check = mcl_minecarts.check_float_time - end - end - end -end - --- Rail direction Handleres local function rail_dir_straight(pos, dir, node) dir = vector.new(dir.x, 0, dir.z) @@ -196,7 +181,6 @@ local BASE_DEF = { after_place_node = function(pos, placer, itemstack, pointed_thing) update_rail_connections(pos) end, - after_destruct = drop_railcarts, _mcl_minecarts = { get_next_dir = rail_dir_straight, }, From 434c55350d33f28aabb7bfa9c0bebab85f5ea54d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 07:12:08 +0000 Subject: [PATCH 088/195] Fix table of contents --- mods/ENTITIES/mcl_minecarts/API.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md index 978654a32..20fd0328c 100644 --- a/mods/ENTITIES/mcl_minecarts/API.md +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -1,12 +1,12 @@ # Table of Contents 1. [Useful Constants](#useful-constants) 2. [Rail](#rail) - 1. [Constants](#constants) - 2. [Functions](#functions) - 3. [Node Definition Options](#node-definition-options) + 1. [Constants](#constants) + 2. [Functions](#functions) + 3. [Node Definition Options](#node-definition-options) 3. [Cart Functions](#cart-functions) 4. [Cart Data Functions](#cart-data-functions) -5. [Cart-Node Interactions](#cart-node-iteractions) +5. [Cart-Node Interactions](#cart-node-interactions) 6. [Train Functions](#train-functions) ## Useful Constants From f7398db6d0940ace8f2d18eb946ce5ed6ace1080 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 08:18:52 +0000 Subject: [PATCH 089/195] More minor changes to API.md, start overall implementation documentation --- mods/ENTITIES/mcl_minecarts/API.md | 7 ++++- mods/ENTITIES/mcl_minecarts/DOC.md | 41 +++++++++++++++++++++++++++ mods/ENTITIES/mcl_minecarts/carts.lua | 3 +- 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 mods/ENTITIES/mcl_minecarts/DOC.md diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md index 20fd0328c..ea1e13f1b 100644 --- a/mods/ENTITIES/mcl_minecarts/API.md +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -25,7 +25,7 @@ second. Defined as 10 blocks/second. - `mcl_minecarts.CART_BLOCKS_SIZE` -The size of blocks to use when searching for carts to respawn. Default is 64. +The size of blocks to use when searching for carts to respawn. Defined as is 64 blocks. - `mcl_minecarts.FRICTION` @@ -77,6 +77,11 @@ Calculate the rail adjacency information for rail placement. Arguments are: - `ignore_neightbor_connections` - if true, don't check that a cart could leave the neighboring node from this direction. +`mcl_minecarts:is_rail(position, railtype)` + +Determines if the node at `position` is a rail. If `railtype` is provided, +determine if the node at `position` is that type of rail. + `mcl_minecarts.register_rail(itemstring, node_definition)` Registers a rail with a few sensible defaults and if a craft recipe was specified, diff --git a/mods/ENTITIES/mcl_minecarts/DOC.md b/mods/ENTITIES/mcl_minecarts/DOC.md new file mode 100644 index 000000000..634bb3f5c --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/DOC.md @@ -0,0 +1,41 @@ + +## On-rail Minecart Movement + +Minecart movement is handled in two distinct regimes: on a rail and off. The +off-rail movement is handled with minetest's builtin entity movement handling. +The on-rail movement is handled with a custom algorithm. This section details +the latter. + +The data for on-rail minecart movement is stored entirely inside mod storage +and indexed by a hex-encoded 128-bit universally-unique identifier (uuid). The +code for handling this storage is in [storage.lua](./storage.lua). This was +done so that minecarts can still move while no players are connected or +when out of range of players. Inspiration for this was the [Adv Trains mod](http://advtrains.de/). +This is a behavior difference when compared to minecraft, as carts there will +stop movement when out of range of players. + +Processing for minecart movement is as follows: +1. In a globalstep handler, determine which carts are moving. +2. Call `do_movement` in [movement.lua](./movement.lua) to update + the cart's location and handle interactions with the environment. + 1. Each movement is broken up into one or more steps that are completely + contained inside a block. This prevents carts from ever jumping from + one rail to another over a gap or thru solid blocks because of server + lag. + 2. Each step uses physically accurate, timestep-independent physics + to move the cart. + 3. As the cart enters and leaves blocks, handlers in nearby blocks are called + to allow the cart to efficiently interact with the environment. +3. If an entity exists for a given cart, the entity will update its position + while loaded in. + +Cart movement when on a rail occurs regarless of whether an entity for that +cart exists or is loaded into memory. As a consequence of this movement, it +is possible for carts with unloaded entities to enter range of a player. +To handle this, periodic checks are performed around players and carts that +are within range but don't have a cart have a new entity spawned. + +Every time a cart has a new entity spawned, it increases a sequence number in +the cart data to allow removing old entities from the minetest engine. Any cart +entity that does not have the current sequence number for a minecart gets removed +once processing for that entity resumes. diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index f6c2a1436..9d7be5b74 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -182,7 +182,6 @@ function DEFAULT_CART_DEF:on_step(dtime) self._staticdata = staticdata end - -- Update entity position local pos = mod.get_cart_position(staticdata) if pos then self.object:move_to(pos) end @@ -551,8 +550,8 @@ minetest.register_globalstep(function(dtime) -- TODO: change how often cart positions are updated based on velocity for uuid,staticdata in mod.carts() do local pos = mod.get_cart_position(staticdata) - local le = mcl_util.get_luaentity_from_uuid(staticdata.uuid) --[[ + local le = mcl_util.get_luaentity_from_uuid(staticdata.uuid) print("cart# "..uuid.. ",velocity="..tostring(staticdata.velocity).. ",pos="..tostring(pos).. From a364fa8112ff976cd6b13eb69245960653665c23 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 08:33:36 +0000 Subject: [PATCH 090/195] More documentation, add myself to copyright list in README.txt --- mods/ENTITIES/mcl_minecarts/DOC.md | 33 +++++++++++++++++--------- mods/ENTITIES/mcl_minecarts/README.txt | 1 + 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/DOC.md b/mods/ENTITIES/mcl_minecarts/DOC.md index 634bb3f5c..1e18e7b63 100644 --- a/mods/ENTITIES/mcl_minecarts/DOC.md +++ b/mods/ENTITIES/mcl_minecarts/DOC.md @@ -7,25 +7,35 @@ The on-rail movement is handled with a custom algorithm. This section details the latter. The data for on-rail minecart movement is stored entirely inside mod storage -and indexed by a hex-encoded 128-bit universally-unique identifier (uuid). The -code for handling this storage is in [storage.lua](./storage.lua). This was -done so that minecarts can still move while no players are connected or -when out of range of players. Inspiration for this was the [Adv Trains mod](http://advtrains.de/). -This is a behavior difference when compared to minecraft, as carts there will -stop movement when out of range of players. +and indexed by a hex-encoded 128-bit universally-unique identifier (uuid). Minecart +entities store this uuid and a sequence identifier. The code for handling this +storage is in [storage.lua](./storage.lua). This was done so that minecarts can +still move while no players are connected or when out of range of players. Inspiration +for this was the [Advanced Trains mod](http://advtrains.de/). This is a behavior difference +when compared to minecraft, as carts there will stop movement when out of range of +players. Processing for minecart movement is as follows: -1. In a globalstep handler, determine which carts are moving. +1. In a globalstep handler in [carts.lua](./carts.lua), determine which carts are + moving. 2. Call `do_movement` in [movement.lua](./movement.lua) to update - the cart's location and handle interactions with the environment. + each cart's location and handle interactions with the environment. 1. Each movement is broken up into one or more steps that are completely contained inside a block. This prevents carts from ever jumping from one rail to another over a gap or thru solid blocks because of server - lag. + lag. Each step is processed with `do_movement_step` 2. Each step uses physically accurate, timestep-independent physics - to move the cart. + to move the cart. Calculating the acceleration to apply to a cart + is broken out into its own function (`calculate_acceperation`). 3. As the cart enters and leaves blocks, handlers in nearby blocks are called - to allow the cart to efficiently interact with the environment. + to allow the cart to efficiently interact with the environment. Handled by + the functions `handle_cart_enter` and `handle_cart_leave` + 4. The cart checks for nearby carts and collides elastically with these. The + calculations for these collisions are in the function `handle_cart_collision` + 5. If the cart enters a new block, determine the new direction the cart will + move with `mcl_minecarts:get_rail_direction` in [functions.lua](./functions.lua]. + The rail nodes provide a hook `_mcl_minecarts.get_next_direction` that + provides this information based on the previous movement direction. 3. If an entity exists for a given cart, the entity will update its position while loaded in. @@ -39,3 +49,4 @@ Every time a cart has a new entity spawned, it increases a sequence number in the cart data to allow removing old entities from the minetest engine. Any cart entity that does not have the current sequence number for a minecart gets removed once processing for that entity resumes. + diff --git a/mods/ENTITIES/mcl_minecarts/README.txt b/mods/ENTITIES/mcl_minecarts/README.txt index 112cbd308..f0f8123ee 100644 --- a/mods/ENTITIES/mcl_minecarts/README.txt +++ b/mods/ENTITIES/mcl_minecarts/README.txt @@ -10,6 +10,7 @@ MIT License Copyright (C) 2012-2016 PilzAdam Copyright (C) 2014-2016 SmallJoker Copyright (C) 2012-2016 Various Minetest developers and contributors +Copyright (C) 2024 teknomunk Authors/licenses of media files: ----------------------- From a9b71ea7ceeb38a5d753621034006fc37fef5557 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 09:17:06 +0000 Subject: [PATCH 091/195] Fix crashes, fix link in documentation --- mods/ENTITIES/mcl_minecarts/DOC.md | 2 +- mods/ENTITIES/mcl_minecarts/init.lua | 2 +- mods/ENTITIES/mcl_minecarts/movement.lua | 3 +-- mods/ENTITIES/mcl_minecarts/storage.lua | 7 ++++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/DOC.md b/mods/ENTITIES/mcl_minecarts/DOC.md index 1e18e7b63..09b2e608c 100644 --- a/mods/ENTITIES/mcl_minecarts/DOC.md +++ b/mods/ENTITIES/mcl_minecarts/DOC.md @@ -33,7 +33,7 @@ Processing for minecart movement is as follows: 4. The cart checks for nearby carts and collides elastically with these. The calculations for these collisions are in the function `handle_cart_collision` 5. If the cart enters a new block, determine the new direction the cart will - move with `mcl_minecarts:get_rail_direction` in [functions.lua](./functions.lua]. + move with `mcl_minecarts:get_rail_direction` in [functions.lua](./functions.lua). The rail nodes provide a hook `_mcl_minecarts.get_next_direction` that provides this information based on the previous movement direction. 3. If an entity exists for a given cart, the entity will update its position diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index dbc5897a4..081e78314 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -5,7 +5,7 @@ local mod = mcl_minecarts mcl_minecarts.modpath = modpath -- Constants -mod.speed_max = 10 +mod.SPEED_MAX = 10 mod.FRICTION = 0.4 mod.MAX_TRAIN_LENGTH = 4 mod.CART_BLOCK_SIZE = 64 diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 3c4ba5e83..2f5acdf71 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -240,7 +240,6 @@ local function calculate_acceleration(staticdata) local pos = staticdata.connected_at local node_name = minetest.get_node(pos).name local node_def = minetest.registered_nodes[node_name] - local max_vel = SPEED_MAX local ctrl = staticdata.controls or {} local time_active = minetest.get_gametime() - 0.25 @@ -251,7 +250,7 @@ local function calculate_acceleration(staticdata) acceleration = -1.5 elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then acceleration = 0.6 - elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then + elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or SPEED_MAX ) then -- Standard friction elseif node_def and node_def._rail_acceleration then acceleration = node_def._rail_acceleration * 4 diff --git a/mods/ENTITIES/mcl_minecarts/storage.lua b/mods/ENTITIES/mcl_minecarts/storage.lua index 91926835d..7f242b7f8 100644 --- a/mods/ENTITIES/mcl_minecarts/storage.lua +++ b/mods/ENTITIES/mcl_minecarts/storage.lua @@ -3,6 +3,7 @@ local mod = mcl_minecarts -- Imports local CART_BLOCK_SIZE = mod.CART_BLOCK_SIZE +assert(CART_BLOCK_SIZE) local cart_data = {} local cart_data_fail_cache = {} @@ -72,9 +73,9 @@ function mod.find_carts_by_block_map(block_map) return cart_list end -function mod.add_block_map(block_map, min_pos, max_pos) - local min = vector.floor(vector.divide(min_pos), CART_BLOCK_SIZE) - local max = vector.floor(vector.divide(max_pos), CART_BLOCK_SIZE) + vector.new(1,1,1) +function mod.add_blocks_to_map(block_map, min_pos, max_pos) + local min = vector.floor(vector.divide(min_pos, CART_BLOCK_SIZE)) + local max = vector.floor(vector.divide(max_pos, CART_BLOCK_SIZE)) + vector.new(1,1,1) for z = min.z,max.z do for y = min.y,max.y do for x = min.x,max.x do From 61a81cc1432202ec5c5f94df9c16285b15bc7852 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 12:14:08 +0000 Subject: [PATCH 092/195] Add documentation on file structure and overviewes of each file --- mods/ENTITIES/mcl_minecarts/DOC.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/DOC.md b/mods/ENTITIES/mcl_minecarts/DOC.md index 09b2e608c..19918abfd 100644 --- a/mods/ENTITIES/mcl_minecarts/DOC.md +++ b/mods/ENTITIES/mcl_minecarts/DOC.md @@ -1,4 +1,29 @@ +## Organization +- [ init.lua](./init.lua) - module entrypoint. The other files are included from here + and several constants are defined here + +- [carts.lua](./carts/lua) - This file contains code related to cart entities, cart + type registration, creation, estruction and updating. The global step function + responsible for updating attached carts is in this file. The various carts are + referenced from this file but actually reside in the subdirectory [carts/](./carts/). + +- [functions.lua](./functions.lua) - This file contains various minecart and rail + utility functions used by the rest of the code. + +- [movement.lua](./movement.lua) - This file contains the code related to cart + movement physics. + +- [rails.lua](./rails.lua) - This file contains code related to rail registation, + placement, connection rules and cart direction selection. This contains the rail + behaviors and the LBM code for updating legacy rail nodes to the new versions + that don't use the railtype render type. + +- [storage.lua](./storage.lua) - This file contains the code than manages minecart + state data to allow processing minecarts while entities are unloaded. + +- [train.lua](./train.lua) - This file contains code related to multi-car trains. + ## On-rail Minecart Movement Minecart movement is handled in two distinct regimes: on a rail and off. The From e208e9c4ceda2b00e905f3cdfd26ecf08df48ecf Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 12:33:22 +0000 Subject: [PATCH 093/195] Add documentation on the rail --- mods/ENTITIES/mcl_minecarts/DOC.md | 45 +++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/DOC.md b/mods/ENTITIES/mcl_minecarts/DOC.md index 19918abfd..00f799e51 100644 --- a/mods/ENTITIES/mcl_minecarts/DOC.md +++ b/mods/ENTITIES/mcl_minecarts/DOC.md @@ -17,13 +17,56 @@ - [rails.lua](./rails.lua) - This file contains code related to rail registation, placement, connection rules and cart direction selection. This contains the rail behaviors and the LBM code for updating legacy rail nodes to the new versions - that don't use the railtype render type. + that don't use the raillike draw type. - [storage.lua](./storage.lua) - This file contains the code than manages minecart state data to allow processing minecarts while entities are unloaded. - [train.lua](./train.lua) - This file contains code related to multi-car trains. +## Rail Nodes + +Previous versions of mcl\_minecarts used one node type for each rail type (standard, +powered, detector and activator) using the raillike draw type that minetest provides. +This version does not use the raillike draw type and instead uses a 1/16th of a block +high nodebox and uses an additional node definition for each variant. The variants +present are: + +- straight +- sloped +- corner +- tee +- cross + +Of the rail types provided by this module, standard has all of these variants. The +remaining types only have straight and sloped variants. + +Unlike the old rail type, this version will only update connections when placed, and +will only place a variant that already has connections into the space the rail is +being placed. Here is how to create the various varients: + +- Straight rail is placed when with zero or one adjacent rail nodes. If no rails + are adjacent, the rail is placed in line with the direction the player is facing. + If there is exactly one adjacent rail present, the straight rail will always rotate + to connect to it. + +- Sloped rail is placed when there are two rails in a straight line, with one being + one block higher. When rail is placed adjacent to a straight rail one block lower + and the rail is facing the block the rail is being placed on, the lower rail will + convert into a slope. + +- A corner rail is placed when there are exactly two adjacent rails that are not in + a line and lead into the space the rail is being placed. The corner will be rotated + to connect these two rails. + +- A tee rail is placed where there are exactly three rails adjact and those existing + rails lead into the the space the new rail is being placed. + +- A rail cross is placed when there is rail in all four adjacent blocks and they all + have a path into the space the new rail is being placed. + +The tee variant will interact with redstone and mesecons to switch the curved section. + ## On-rail Minecart Movement Minecart movement is handled in two distinct regimes: on a rail and off. The From 9f192e87f331eac397067ee0a47a723bf9011158 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 13:09:24 +0000 Subject: [PATCH 094/195] Modify mcl_entity_invs to add support for save/load items hooks in entities, add save/load hooks to minecarts to store item list in the minecart data and not in the entity data so that respawn doesn't destroy items --- mods/ENTITIES/mcl_entity_invs/init.lua | 13 ++++++++++--- mods/ENTITIES/mcl_minecarts/carts.lua | 11 ++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_entity_invs/init.lua b/mods/ENTITIES/mcl_entity_invs/init.lua index 35af491e1..601ec2019 100644 --- a/mods/ENTITIES/mcl_entity_invs/init.lua +++ b/mods/ENTITIES/mcl_entity_invs/init.lua @@ -35,7 +35,9 @@ function mcl_entity_invs.load_inv(ent,size) mcl_log("load_inv 3") inv = minetest.create_detached_inventory(ent._inv_id, inv_callbacks) inv:set_size("main", size) - if ent._items then + if ent._mcl_entity_invs_load_items then + inv:set_list("main",ent:_mcl_entity_invs_load_items()) + elseif ent._items then inv:set_list("main",ent._items) end else @@ -46,9 +48,14 @@ end function mcl_entity_invs.save_inv(ent) if ent._inv then - ent._items = {} + local items = {} for i,it in ipairs(ent._inv:get_list("main")) do - ent._items[i] = it:to_string() + items[i] = it:to_string() + end + if ent._mcl_entity_invs_save_items then + ent:_mcl_entity_invs_save_items(items) + else + ent._items = items end minetest.remove_detached_inventory(ent._inv_id) ent._inv = nil diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 9d7be5b74..bd86676f8 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -143,6 +143,16 @@ function DEFAULT_CART_DEF:get_staticdata() return minetest.serialize({uuid = self._staticdata.uuid, seq=self._seq}) end +function DEFAULT_CART_DEF:_mcl_entity_invs_load_items() + local staticdata = self._staticdata + return staticdata.inventory or {} +end +function DEFAULT_CART_DEF:_mcl_entity_invs_save_items(items) + local staticdata = self._staticdata + print("Saving entity inventory items="..dump(items)) + staticdata.inventory = table.copy(items) +end + function DEFAULT_CART_DEF:add_node_watch(pos) local staticdata = self._staticdata local watches = staticdata.node_watches or {} @@ -246,7 +256,6 @@ function DEFAULT_CART_DEF:on_step(dtime) end mod.update_cart_orientation(self) - end function mod.kill_cart(staticdata) local pos From 7a1e0d9a52c66287b71cd700303072f52f24ba1b Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 13:49:00 +0000 Subject: [PATCH 095/195] Fix sloped power,activator and detector rails, remove debug print --- mods/ENTITIES/mcl_minecarts/carts.lua | 1 - mods/ENTITIES/mcl_minecarts/rails.lua | 69 +++++++++++++++++++++------ 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index bd86676f8..11d802e1f 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -149,7 +149,6 @@ function DEFAULT_CART_DEF:_mcl_entity_invs_load_items() end function DEFAULT_CART_DEF:_mcl_entity_invs_save_items(items) local staticdata = self._staticdata - print("Saving entity inventory items="..dump(items)) staticdata.inventory = table.copy(items) end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 632e7859e..bc91cc3f0 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -261,11 +261,12 @@ function mod.register_straight_rail(base_name, tiles, def) }) -- Sloped variant - mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ + mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(sloped_def),{ description = S("Sloped Rail"), -- Temporary name to make debugging easier _mcl_minecarts = { get_next_dir = rail_dir_sloped, }, + mesecons = def.mesecons_sloped, tiles = { tiles[1] }, _mcl_minecarts = { railtype = "sloped", @@ -444,7 +445,15 @@ mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_ conductor = { state = mesecon.state.off, offstate = "mcl_minecarts:golden_rail_v2", - onstate = "mcl_minecarts:golden_rail_v2_on", + onstate = "mcl_minecarts:golden_rail_v2_on", + rules = rail_rules_long, + }, + }, + mesecons_sloped = { + conductor = { + state = mesecon.state.off, + offstate = "mcl_minecarts:golden_rail_v2_sloepd", + onstate = "mcl_minecarts:golden_rail_v2_on_sloped", rules = rail_rules_long, }, }, @@ -471,7 +480,15 @@ mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_ra conductor = { state = mesecon.state.on, offstate = "mcl_minecarts:golden_rail_v2", - onstate = "mcl_minecarts:golden_rail_v2_on", + onstate = "mcl_minecarts:golden_rail_v2_on", + rules = rail_rules_long, + }, + }, + mesecons_sloped = { + conductor = { + state = mesecon.state.on, + offstate = "mcl_minecarts:golden_rail_v2_sloped", + onstate = "mcl_minecarts:golden_rail_v2_on_sloped", rules = rail_rules_long, }, }, @@ -488,7 +505,15 @@ mod.register_straight_rail("mcl_minecarts:activator_rail_v2", {"mcl_minecarts_ra conductor = { state = mesecon.state.off, offstate = "mcl_minecarts:activator_rail_v2", - onstate = "mcl_minecarts:activator_rail_v2_on", + onstate = "mcl_minecarts:activator_rail_v2_on", + rules = rail_rules_long, + }, + }, + mesecons_sloped = { + conductor = { + state = mesecon.state.off, + offstate = "mcl_minecarts:activator_rail_v2_sloped", + onstate = "mcl_minecarts:activator_rail_v2_on_sloped", rules = rail_rules_long, }, }, @@ -503,6 +528,16 @@ mod.register_straight_rail("mcl_minecarts:activator_rail_v2", {"mcl_minecarts_ra }) -- Activator rail (on) +local function activator_rail_action_on(pos, node) + local pos2 = { x = pos.x, y =pos.y + 1, z = pos.z } + local objs = minetest.get_objects_inside_radius(pos2, 1) + for _, o in pairs(objs) do + local l = o:get_luaentity() + if l and string.sub(l.name, 1, 14) == "mcl_minecarts:" and l.on_activate_by_rail then + l:on_activate_by_rail() + end + end +end mod.register_straight_rail("mcl_minecarts:activator_rail_v2_on", {"mcl_minecarts_rail_activator_powered.png"},{ _doc_items_create_entry = false, groups = { @@ -512,21 +547,25 @@ mod.register_straight_rail("mcl_minecarts:activator_rail_v2_on", {"mcl_minecarts conductor = { state = mesecon.state.on, offstate = "mcl_minecarts:activator_rail_v2", - onstate = "mcl_minecarts:activator_rail_v2_on", + onstate = "mcl_minecarts:activator_rail_v2_on", rules = rail_rules_long, }, effector = { -- Activate minecarts - action_on = function(pos, node) - local pos2 = { x = pos.x, y =pos.y + 1, z = pos.z } - local objs = minetest.get_objects_inside_radius(pos2, 1) - for _, o in pairs(objs) do - local l = o:get_luaentity() - if l and string.sub(l.name, 1, 14) == "mcl_minecarts:" and l.on_activate_by_rail then - l:on_activate_by_rail() - end - end - end, + action_on = activator_rail_action_on, + }, + + }, + mesecons_sloped = { + conductor = { + state = mesecon.state.on, + offstate = "mcl_minecarts:activator_rail_v2_sloped", + onstate = "mcl_minecarts:activator_rail_v2_on_sloped", + rules = rail_rules_long, + }, + effector = { + -- Activate minecarts + action_on = activator_rail_action_on, }, }, From eae9a2b6ad23509a33de9fb0bd12651fc34a44fa Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 11 Apr 2024 14:02:53 +0000 Subject: [PATCH 096/195] Stop rail from being placed directly above rail (floating in air) --- mods/ENTITIES/mcl_minecarts/rails.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index bc91cc3f0..f09a702ad 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -178,6 +178,15 @@ local BASE_DEF = { _tt_help = S("Track for minecarts"), _doc_items_usagehelp = railuse, _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), + on_place = function(itemstack, placer, pointed_thing) + local node_name = minetest.get_node(pointed_thing.under).name + -- Don't allow placing rail above rail + if minetest.get_item_group(node_name,"rail") == 0 then + return minetest.item_place_node(itemstack, placer, pointed_thing) + else + return itemstack + end + end, after_place_node = function(pos, placer, itemstack, pointed_thing) update_rail_connections(pos) end, From 04d29e8f2b49c36edd0d97feef6b765d03baa72a Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 07:37:34 +0000 Subject: [PATCH 097/195] Give carts a small vertical lift when pushed to allow them to get back on rails --- mods/ENTITIES/mcl_minecarts/movement.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 2f5acdf71..198a2fede 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -494,6 +494,11 @@ local function do_detached_movement(self, dtime) if away then local v = self.object:get_velocity() self.object:set_velocity(v - away) + + -- Boost the minecart vertically a bit to get over the edge of rails and things like carpets + local boost = vector.offset(vector.multiply(vector.normalize(away), 0.1), 0, 0.07, 0) -- 1/16th + 0.0075 + local pos = self.object:get_pos() + self.object:set_pos(vector.add(pos,boost)) end -- Try to reconnect to rail From d93cf5d4f5aa231962055d598b95968a8cbbe929 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 09:11:46 +0000 Subject: [PATCH 098/195] Update mineshafts for new rail and minecarts, add loot to generated chest and hopper minecarts (and remove notes about a hack) --- mods/ENTITIES/mcl_minecarts/carts.lua | 22 ++++--- mods/MAPGEN/tsm_railcorridors/gameconfig.lua | 65 +++++++++++++++----- mods/MAPGEN/tsm_railcorridors/init.lua | 63 ++++++++----------- 3 files changed, 91 insertions(+), 59 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 11d802e1f..63dde3a53 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -309,6 +309,20 @@ function DEFAULT_CART_DEF:on_death(killer) kill_cart(self._staticdata) end +-- Create a minecart +function mod.create_minecart(entity_id, pos, dir) + -- Setup cart data + local uuid = mcl_util.gen_uuid() + data = make_staticdata( nil, pos, dir ) + data.uuid = uuid + data.cart_type = entity_id + update_cart_data(data) + save_cart_data(uuid) + + return uuid +end +local create_minecart = mod.create_minecart + -- Place a minecart at pointed_thing function mod.place_minecart(itemstack, pointed_thing, placer) if not pointed_thing.type == "node" then @@ -332,13 +346,7 @@ function mod.place_minecart(itemstack, pointed_thing, placer) local entity_id = entity_mapping[itemstack:get_name()] - -- Setup cart data - local uuid = mcl_util.gen_uuid() - data = make_staticdata( nil, railpos, cart_dir ) - data.uuid = uuid - data.cart_type = entity_id - update_cart_data(data) - save_cart_data(uuid) + local uuid = create_minecart(entity_id, railpos, cart_dir) -- Create the entity with the staticdata already setup local sd = minetest.serialize({ uuid=uuid, seq=1 }) diff --git a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua index 9f924f00b..b5aedec0b 100644 --- a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua +++ b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua @@ -3,17 +3,38 @@ -- Adapted for MineClone 2! +-- Imports +local create_minecart = mcl_minecarts.create_minecart +local get_cart_data = mcl_minecarts.get_cart_data +local save_cart_data = mcl_minecarts.save_cart_data + -- Node names (Don't use aliases!) tsm_railcorridors.nodes = { dirt = "mcl_core:dirt", chest = "mcl_chests:chest", - rail = "mcl_minecarts:rail", + rail = "mcl_minecarts:rail_v2", torch_floor = "mcl_torches:torch", torch_wall = "mcl_torches:torch_wall", cobweb = "mcl_core:cobweb", spawner = "mcl_mobspawners:spawner", } +local update_rail_connections = mcl_minecarts.update_rail_connections +local rails_to_update = {} +tsm_railcorridors.on_place_node = { + [tsm_railcorridors.nodes.rail] = function(pos, node) + rails_to_update[#rails_to_update + 1] = pos + end, +} +tsm_railcorridors.on_start = function() + rails_to_update = {} +end +tsm_railcorridors.on_finish = function() + for _,pos in pairs(rails_to_update) do + update_rail_connections(pos, {legacy = true, ignore_neighbor_connections = true}) + end +end + local mg_name = minetest.get_mapgen_setting("mg_name") if mg_name == "v6" then @@ -40,6 +61,9 @@ tsm_railcorridors.carts = { "mcl_minecarts:minecart", "mcl_minecarts:minecart", "mcl_minecarts:chest_minecart", "mcl_minecarts:chest_minecart", "mcl_minecarts:tnt_minecart" +local has_loot = { + ["mcl_minecarts:chest_minecart"] = true, + ["mcl_minecarts:hopper_minceart"] = true, } -- This is called after a spawner has been placed by the game. @@ -50,19 +74,32 @@ function tsm_railcorridors.on_construct_spawner(pos) mcl_mobspawners.setup_spawner(pos, "mobs_mc:cave_spider", 0, 7) end - -- This is called after a cart has been placed by the game. -- Use this to properly set up entity metadata and stuff. +-- * entity_id - type of cart to create -- * pos: Position of cart --- * cart: Cart entity -function tsm_railcorridors.on_construct_cart(_, cart, pr_carts) - local l = cart:get_luaentity() - local inv = mcl_entity_invs.load_inv(l,27) - if inv then -- otherwise probably not a chest minecart - local items = tsm_railcorridors.get_treasures(pr_carts) - mcl_loot.fill_inventory(inv, "main", items, pr_carts) - mcl_entity_invs.save_inv(l) +-- * pr: pseudorandom +function tsm_railcorridors.create_cart_staticdata(entity_id, pos, pr) + local uuid = create_minecart(entity_id, pos, vector.new(1,0,0)) + + -- Fill the cart with loot + local cartdata = get_cart_data(uuid) + if cartdata and has_loot[entity_id] then + local items = tsm_railcorridors.get_treasures(pr) + + -- TODO: determine if we should convert to use mcl_loot + -- mcl_loot.fill_inventory(inv, "main", items, pr_carts) + -- Convert from ItemStack to itemstrings + for k,item in pairs(items) do + items[k] = item:to_string() + end + cartdata.inventory = items + + print("cartdata = "..dump(cartdata)) + save_cart_data(uuid) end + + return minetest.serialize({ uuid=uuid, seq=1 }) end -- Fallback function. Returns a random treasure. This function is called for chests @@ -110,11 +147,11 @@ function tsm_railcorridors.get_treasures(pr) stacks_min = 3, stacks_max = 3, items = { - { itemstring = "mcl_minecarts:rail", weight = 20, amount_min = 4, amount_max = 8 }, + { itemstring = "mcl_minecarts:rail_v2", weight = 20, amount_min = 4, amount_max = 8 }, { itemstring = "mcl_torches:torch", weight = 15, amount_min = 1, amount_max = 16 }, - { itemstring = "mcl_minecarts:activator_rail", weight = 5, amount_min = 1, amount_max = 4 }, - { itemstring = "mcl_minecarts:detector_rail", weight = 5, amount_min = 1, amount_max = 4 }, - { itemstring = "mcl_minecarts:golden_rail", weight = 5, amount_min = 1, amount_max = 4 }, + { itemstring = "mcl_minecarts:activator_rail_v2", weight = 5, amount_min = 1, amount_max = 4 }, + { itemstring = "mcl_minecarts:detector_rail_v2", weight = 5, amount_min = 1, amount_max = 4 }, + { itemstring = "mcl_minecarts:golden_rail_v2", weight = 5, amount_min = 1, amount_max = 4 }, } }, -- non-MC loot: 50% chance to add a minecart, offered as alternative to spawning minecarts on rails. diff --git a/mods/MAPGEN/tsm_railcorridors/init.lua b/mods/MAPGEN/tsm_railcorridors/init.lua index 9895ab44c..1ff80fb4d 100644 --- a/mods/MAPGEN/tsm_railcorridors/init.lua +++ b/mods/MAPGEN/tsm_railcorridors/init.lua @@ -1,7 +1,9 @@ local pairs = pairs local tonumber = tonumber -tsm_railcorridors = {} +tsm_railcorridors = { + after = {}, +} -- Load node names dofile(minetest.get_modpath(minetest.get_current_modname()).."/gameconfig.lua") @@ -169,6 +171,10 @@ local function SetNodeIfCanBuild(pos, node, check_above, can_replace_rail) (can_replace_rail and name == tsm_railcorridors.nodes.rail) ) then minetest.set_node(pos, node) + local after = tsm_railcorridors.on_place_node[node.name] + if after then + after(pos, node) + end return true else return false @@ -392,30 +398,6 @@ local function PlaceChest(pos, param2) end end --- This function checks if a cart has ACTUALLY been spawned. --- To be calld by minetest.after. --- This is a workaround thanks to the fact that minetest.add_entity is unreliable as fuck --- See: https://github.com/minetest/minetest/issues/4759 --- FIXME: Kill this horrible hack with fire as soon you can. -local RecheckCartHack = nil -if not minetest.features.random_state_restore then -- proxy for minetest > 5.9.0, this feature will not be removed -RecheckCartHack = function(params) - local pos = params[1] - local cart_id = params[2] - -- Find cart - for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do - if obj ~= nil and obj:get_luaentity().name == cart_id then - -- Cart found! We can now safely call the callback func. - -- (calling it earlier has the danger of failing) - minetest.log("info", "[tsm_railcorridors] Cart spawn succeeded: "..minetest.pos_to_string(pos)) - tsm_railcorridors.on_construct_cart(pos, obj, pr_carts) - return - end - end - minetest.log("info", "[tsm_railcorridors] Cart spawn FAILED: "..minetest.pos_to_string(pos)) -end -end - -- Try to place a cobweb. -- pos: Position of cobweb -- needs_check: If true, checks if any of the nodes above, below or to the side of the cobweb. @@ -938,17 +920,13 @@ local function spawn_carts() -- See local cart_id = tsm_railcorridors.carts[cart_type] minetest.log("info", "[tsm_railcorridors] Cart spawn attempt: "..minetest.pos_to_string(cpos)) - local obj = minetest.add_entity(cpos, cart_id) + local cart_staticdata = nil - -- This checks if the cart is actually spawned, it's a giant hack! - -- Note that the callback function is also called there. - -- TODO: Move callback function to this position when the - -- minetest.add_entity bug has been fixed (supposedly in 5.9.0?) - if RecheckCartHack then - minetest.after(3, RecheckCartHack, {cpos, cart_id}) - else - tsm_railcorridors.on_construct_cart(cpos, obj, pr_carts) - end + -- Try to create cart staticdata + local hook = tsm_railcorridors.create_cart_staticdata + if hook then cart_staticdata = hook(cart_id, cpos, pr) end + + minetest.add_entity(cpos, cart_id, cart_staticdata) end end carts_table = {} @@ -957,7 +935,7 @@ end -- Start generation of a rail corridor system -- main_cave_coords is the center of the floor of the dirt room, from which -- all corridors expand. -local function create_corridor_system(main_cave_coords) +local function create_corridor_system(main_cave_coords, pr) -- Dirt room size local maxsize = 6 @@ -1112,13 +1090,22 @@ mcl_structures.register_structure("mineshaft",{ y_min = mcl_vars.mg_overworld_min, place_func = function(pos,_,pr,blockseed) local r = pr:next(-50,-10) + r = -10 local p = vector.offset(pos,0,r,0) if p.y < mcl_vars.mg_overworld_min + 5 then p.y = mcl_vars.mg_overworld_min + 5 end - if p.y > -10 then return true end + --if p.y > -10 then return true end InitRandomizer(blockseed) - create_corridor_system(p) + + local hook = tsm_railcorridors.on_start + if hook then hook() end + + create_corridor_system(p, pr) + + local hook = tsm_railcorridors.on_finish + if hook then hook() end + return true end, From 712f0b9b9b83206d44307a894de5610e80eac33c Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 12:28:24 +0000 Subject: [PATCH 099/195] Fix hopper-minecart interaction, convert ipairs(table) to use for i=1,#table instead --- mods/ENTITIES/mcl_minecarts/carts.lua | 51 ++++-- .../mcl_minecarts/carts/with_hopper.lua | 4 +- mods/ENTITIES/mcl_minecarts/functions.lua | 9 +- mods/ENTITIES/mcl_minecarts/movement.lua | 11 +- mods/ITEMS/mcl_hoppers/init.lua | 154 ++++++++++++------ 5 files changed, 158 insertions(+), 71 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 63dde3a53..26a95f826 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -156,8 +156,8 @@ function DEFAULT_CART_DEF:add_node_watch(pos) local staticdata = self._staticdata local watches = staticdata.node_watches or {} - for _,watch in ipairs(watches) do - if watch == pos then return end + for i=1,#watches do + if watches[i] == pos then return end end watches[#watches+1] = pos @@ -168,9 +168,10 @@ function DEFAULT_CART_DEF:remove_node_watch(pos) local watches = staticdata.node_watches or {} local new_watches = {} - for _,node_pos in ipairs(watches) do + for i=1,#watches do + local node_pos = watches[i] if node_pos ~= pos then - new_watches[#new_watches] = node_pos + new_watches[#new_watches + 1] = node_pos end end staticdata.node_watches = new_watches @@ -256,7 +257,7 @@ function DEFAULT_CART_DEF:on_step(dtime) mod.update_cart_orientation(self) end -function mod.kill_cart(staticdata) +function mod.kill_cart(staticdata, killer) local pos minetest.log("action", "cart #"..staticdata.uuid.." was killed") @@ -288,16 +289,33 @@ function mod.kill_cart(staticdata) -- Drop items if not staticdata.dropped then + + -- Try to drop the cart local entity_def = minetest.registered_entities[staticdata.cart_type] if entity_def then - local drop = entity_def.drop - for d=1, #drop do - minetest.add_item(pos, drop[d]) + local drop_cart = true + if killer and minetest.is_creative_enabled(killer:get_player_name()) then + drop_cart = false end - -- Prevent item duplication - staticdata.dropped = true + if drop_cart then + local drop = entity_def.drop + for d=1, #drop do + minetest.add_item(pos, drop[d]) + end + end end + + -- Drop any items in the inventory + local inventory = staticdata.inventory + if inventory then + for i=1,#inventory do + minetest.add_item(pos, inventory[i]) + end + end + + -- Prevent item duplication + staticdata.dropped = true end -- Remove data @@ -306,7 +324,7 @@ end local kill_cart = mod.kill_cart function DEFAULT_CART_DEF:on_death(killer) - kill_cart(self._staticdata) + kill_cart(self._staticdata, killer) end -- Create a minecart @@ -551,6 +569,8 @@ end local timer = 0 minetest.register_globalstep(function(dtime) + + -- Periodically respawn carts that come into range of a player timer = timer - dtime if timer <= 0 then local start_time = minetest.get_us_time() @@ -559,11 +579,13 @@ minetest.register_globalstep(function(dtime) local duration = (stop_time - start_time) / 1e6 timer = duration / 250e-6 -- Schedule 50us per second if timer > 5 then timer = 5 end - --print("Took "..tostring(duration).." seconds, rescheduling for "..tostring(timer).." seconds in the future") end -- Handle periodically updating out-of-range carts -- TODO: change how often cart positions are updated based on velocity + local start_time + if DEBUG then start_time = minetest.get_us_time() end + for uuid,staticdata in mod.carts() do local pos = mod.get_cart_position(staticdata) --[[ @@ -580,5 +602,10 @@ minetest.register_globalstep(function(dtime) do_movement(staticdata, dtime) end end + + if DEBUG then + local stop_time = minetest.get_us_time() + print("Update took "..((stop_time-start_time)*1e-6).." seconds") + end end) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua b/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua index cb16b4cc6..ac86cfe7f 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua @@ -156,13 +156,13 @@ mod.register_minecart({ groups = { container = 1 }, on_rightclick = nil, on_activate_by_rail = nil, - _mcl_minecarts_on_enter = function(self, pos) - local staticdata = self._staticdata + _mcl_minecarts_on_enter = function(self, pos, staticdata) if (staticdata.hopper_delay or 0) > 0 then return end -- try to pull from containers into our inventory + if not self then return end local inv = mcl_entity_invs.load_inv(self,5) local above_pos = pos + vector.new(0,1,0) mcl_util.hopper_pull_to_inventory(inv, 'main', above_pos, pos) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index c4694cc06..556af3dc5 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -185,7 +185,8 @@ local function get_rail_connections(pos, opt) local ignore_neighbor_connections = opt and opt.ignore_neighbor_connections local connections = 0 - for i,dir in ipairs(CONNECTIONS) do + for i = 1,#CONNECTIONS do + dir = CONNECTIONS[i] local neighbor = vector.add(pos, dir) local node = minetest.get_node(neighbor) local nodedef = minetest.registered_nodes[node.name] @@ -223,7 +224,8 @@ local function update_rail_connections(pos, opt) local connections = get_rail_connections(pos, opt) -- Check for rasing rails to slopes - for i,dir in ipairs(CONNECTIONS) do + for i = 1,#CONNECTIONS do + local dir = CONNECTIONS[i] local neighbor = vector.add(pos, dir) make_sloped_if_straight( vector.offset(neighbor, 0, -1, 0), dir ) end @@ -258,7 +260,8 @@ local function update_rail_connections(pos, opt) local node_def = minetest.registered_nodes[node.name] if get_path(node_def, "_mcl_minecarts", "can_slope") then - for _,dir in ipairs(CONNECTIONS) do + for i=1,#CONNECTIONS do + local dir = CONNECTIONS[i] local higher_rail_pos = vector.offset(pos,dir.x,1,dir.z) local rev_dir = vector.direction(dir,vector.new(0,0,0)) if mcl_minecarts:is_rail(higher_rail_pos) and is_connection(higher_rail_pos, rev_dir) then diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 198a2fede..f750a4ad0 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -55,7 +55,9 @@ local function handle_cart_enter_exit(staticdata, pos, next_dir, event) local dir = staticdata.dir local right = vector.new( dir.z, dir.y, -dir.x) local up = vector.new(0,1,0) - for _,check in ipairs(enter_exit_checks) do + for i=1,#enter_exit_checks do + local check = enter_exit_checks[i] + local check_pos = pos + dir * check[1] + right * check[2] + up * check[3] local node = minetest.get_node(check_pos) local node_def = minetest.registered_nodes[node.name] @@ -74,7 +76,7 @@ local function handle_cart_enter_exit(staticdata, pos, next_dir, event) -- Handle cart-specific behaviors if luaentity then local hook = luaentity["_mcl_minecarts_"..event] - if hook then hook(self, pos) end + if hook then hook(luaentity, pos, staticdata) end else --minetest.log("warning", "TODO: change _mcl_minecarts_"..event.." calling so it is not dependent on the existence of a luaentity") end @@ -99,12 +101,13 @@ local function handle_cart_node_watches(staticdata, dtime) local watches = staticdata.node_watches or {} local new_watches = {} local luaentity = mcl_util.get_luaentity_from_uuid(staticdata.uuid) - for _,node_pos in ipairs(watches) do + for i=1,#watches do + local node_pos = watches[i] local node = minetest.get_node(node_pos) local node_def = minetest.registered_nodes[node.name] if node_def then local hook = node_def._mcl_minecarts_node_on_step - if hook and hook(node_pos, luaentity, dtime) then + if hook and hook(node_pos, luaentity, dtime, staticdata) then new_watches[#new_watches+1] = node_pos end end diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index 0ba1ca9a6..73860cad1 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -136,6 +136,47 @@ local function hopper_push_to_mc(mc_ent, dest_pos, inv_size) end end end +local function hopper_pull_from_mc(mc_ent, dest_pos, inv_size) + local inv = mcl_entity_invs.load_inv(mc_ent, inv_size) + if not inv then + mcl_log("No inv") + return false + end + + local dest_meta = minetest.get_meta(dest_pos) + local dest_inv = dest_meta:get_inventory() + if not dest_inv then + mcl_log("No dest inv") + return false + end + + mcl_log("inv. size: " .. mc_ent._inv_size) + for i = 1, mc_ent._inv_size, 1 do + local stack = inv:get_stack("main", i) + + mcl_log("i: " .. tostring(i)) + mcl_log("Name: [" .. tostring(stack:get_name()) .. "]") + mcl_log("Count: " .. tostring(stack:get_count())) + mcl_log("stack max: " .. tostring(stack:get_stack_max())) + + if not stack:get_name() or stack:get_name() ~= "" then + if dest_inv:room_for_item("main", stack:peek_item()) then + mcl_log("Room so unload") + dest_inv:add_item("main", stack:take_item()) + inv:set_stack("main", i, stack) + + -- Take one item and stop until next time, report that we took something + return true + else + mcl_log("no Room") + end + + else + mcl_log("nothing there") + end + end +end +mcl_hoppers.pull_from_minecart = hopper_pull_from_mc -- Downwards hopper (base definition) @@ -243,16 +284,29 @@ local def_hopper = { minetest.log("action", player:get_player_name() .. " takes stuff from mcl_hoppers at " .. minetest.pos_to_string(pos)) end, - _mcl_minecarts_on_enter_above = function(pos, cart, next_dir) - -- Only push to containers - if cart.groups and (cart.groups.container or 0) ~= 0 then + _mcl_minecarts_on_enter_below = function(pos, cart, next_dir) + print("Cart entered above "..tostring(pos)) + -- Only pull to containers + if cart and cart.groups and (cart.groups.container or 0) ~= 0 then cart:add_node_watch(pos) + hopper_push_to_mc(cart, pos, 5) + end + end, + _mcl_minecarts_on_enter_above = function(pos, cart, next_dir) + print("Cart entered below "..tostring(pos)) + + -- Only push to containers + if cart and cart.groups and (cart.groups.container or 0) ~= 0 then + cart:add_node_watch(pos) + hopper_pull_from_mc(cart, pos, 5) end end, _mcl_minecarts_on_leave_above = function(pos, cart, next_dir) + if not cart then return end + cart:remove_node_watch(pos) end, - _mcl_minecarts_node_on_step = function(pos, cart, dtime) + _mcl_minecarts_node_on_step = function(pos, cart, dtime, cartdata) if not cart then minetest.log("warning", "trying to process hopper-to-minecart movement without luaentity") return @@ -260,9 +314,18 @@ local def_hopper = { local meta = minetest.get_meta(pos) + local cart_pos = mcl_minecarts.get_cart_position(cartdata) + local timer = meta:get_int("minecart_hopper_timer") if timer < dtime then - hopper_push_to_mc(cart, pos, 5) + if vector.direction(pos,cart_pos).y > 0 then + -- The cart is above us, pull from minecart + print("Pulling from cart above "..tostring(pos)) + hopper_pull_from_mc(cart, pos, 5) + else + print("Pushing to cart below "..tostring(pos)) + hopper_push_to_mc(cart, pos, 5) + end timer = timer + 1 else timer = timer - dtime @@ -476,7 +539,23 @@ local def_hopper_side = { on_rotate = on_rotate, sounds = mcl_sounds.node_sound_metal_defaults(), + _mcl_minecarts_on_enter_below = function(pos, cart, next_dir) + print("Cart entered above "..tostring(pos)..",cart="..tostring(cart)) + + -- Only push to containers + if cart and cart.groups and (cart.groups.container or 0) ~= 0 then + print("Pulling from cart above "..tostring(pos)) + cart:add_node_watch(pos) + hopper_pull_from_mc(cart, pos, 5) + end + end, + _mcl_minecarts_on_leave_below = function(pos, cart, next_dir) + if not cart then return end + cart:remove_node_watch(pos) + end, _mcl_minecarts_on_enter_side = function(pos, cart, next_dir, rail_pos) + if not cart then return end + -- Only try to push to minecarts when the spout position is pointed at the rail local face = minetest.get_node(pos).param2 local dst_pos = {} @@ -497,20 +576,37 @@ local def_hopper_side = { end end, _mcl_minecarts_on_leave_side = function(pos, cart, next_dir) + if not cart then return end + cart:remove_node_watch(pos) end, - _mcl_minecarts_node_on_step = function(pos, cart, dtime) - local meta = minetest.get_meta(pos) + _mcl_minecarts_node_on_step = function(pos, cart, dtime, cartdata) + if not cart then return end + local tick = false + local meta = minetest.get_meta(pos) local timer = meta:get_int("minecart_hopper_timer") + print("dtime="..dtime..",timer="..timer) if timer < dtime then - hopper_push_to_mc(cart, pos, 5) + tick = true timer = timer + 1 else timer = timer - dtime end meta:set_int("minecart_hopper_timer", timer) + if tick then + local cart_pos = mcl_minecarts.get_cart_position(cartdata) + if not cart_pos then return false end + + print("uuid="..cartdata.uuid) + if cart_pos.y == pos.y then + hopper_push_to_mc(cart, pos, 5) + elseif cart_pos.y > pos.y then + hopper_pull_from_mc(cart, pos, 5) + end + end + return true end, @@ -546,48 +642,6 @@ minetest.register_node("mcl_hoppers:hopper_side_disabled", def_hopper_side_disab --[[ END OF NODE DEFINITIONS ]] -local function hopper_pull_from_mc(mc_ent, dest_pos, inv_size) - local inv = mcl_entity_invs.load_inv(mc_ent, inv_size) - if not inv then - mcl_log("No inv") - return false - end - - local dest_meta = minetest.get_meta(dest_pos) - local dest_inv = dest_meta:get_inventory() - if not dest_inv then - mcl_log("No dest inv") - return false - end - - mcl_log("inv. size: " .. mc_ent._inv_size) - for i = 1, mc_ent._inv_size, 1 do - local stack = inv:get_stack("main", i) - - mcl_log("i: " .. tostring(i)) - mcl_log("Name: [" .. tostring(stack:get_name()) .. "]") - mcl_log("Count: " .. tostring(stack:get_count())) - mcl_log("stack max: " .. tostring(stack:get_stack_max())) - - if not stack:get_name() or stack:get_name() ~= "" then - if dest_inv:room_for_item("main", stack:peek_item()) then - mcl_log("Room so unload") - dest_inv:add_item("main", stack:take_item()) - inv:set_stack("main", i, stack) - - -- Take one item and stop until next time, report that we took something - return true - else - mcl_log("no Room") - end - - else - mcl_log("nothing there") - end - end -end -mcl_hoppers.pull_from_minecart = hopper_pull_from_mc - --[[ BEGIN OF ABM DEFINITONS ]] minetest.register_abm({ From e1ff3942048d3dafa8351243112258cc44ed0d10 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 13:31:01 +0000 Subject: [PATCH 100/195] Create mcl_util.metadata_timer, fix crashes, add checks to prevent hoppers from pulling from carts that are not in the square above it --- mods/CORE/mcl_util/init.lua | 12 +++++++ mods/ITEMS/mcl_hoppers/init.lua | 56 ++++++++++----------------------- 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index ec58fc566..9daa885d9 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -861,4 +861,16 @@ function mcl_util.assign_uuid(obj) return le._uuid end +function mcl_util.metadata_timer(meta, name, dtime) + local tick = false + local timer = meta:get_float(name) + if timer < dtime then + tick = true + timer = timer + 1 + else + timer = timer - dtime + end + meta:set_float(name, timer) + if not tick then return true end +end diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index 73860cad1..cf39c760a 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -285,7 +285,6 @@ local def_hopper = { " takes stuff from mcl_hoppers at " .. minetest.pos_to_string(pos)) end, _mcl_minecarts_on_enter_below = function(pos, cart, next_dir) - print("Cart entered above "..tostring(pos)) -- Only pull to containers if cart and cart.groups and (cart.groups.container or 0) ~= 0 then cart:add_node_watch(pos) @@ -293,8 +292,6 @@ local def_hopper = { end end, _mcl_minecarts_on_enter_above = function(pos, cart, next_dir) - print("Cart entered below "..tostring(pos)) - -- Only push to containers if cart and cart.groups and (cart.groups.container or 0) ~= 0 then cart:add_node_watch(pos) @@ -312,25 +309,18 @@ local def_hopper = { return end - local meta = minetest.get_meta(pos) + if not mcl_util.metadata_timer(minetest.get_meta(pos), "minecart_hopper_timer", dtime) then return end local cart_pos = mcl_minecarts.get_cart_position(cartdata) + if not cart_pos then return false end + if pos.x ~= cart_pos.x or pos.z ~= cart_pos.z then return end - local timer = meta:get_int("minecart_hopper_timer") - if timer < dtime then - if vector.direction(pos,cart_pos).y > 0 then - -- The cart is above us, pull from minecart - print("Pulling from cart above "..tostring(pos)) - hopper_pull_from_mc(cart, pos, 5) - else - print("Pushing to cart below "..tostring(pos)) - hopper_push_to_mc(cart, pos, 5) - end - timer = timer + 1 + if vector.direction(pos,cart_pos).y > 0 then + -- The cart is above us, pull from minecart + hopper_pull_from_mc(cart, pos, 5) else - timer = timer - dtime + hopper_push_to_mc(cart, pos, 5) end - meta:set_int("minecart_hopper_timer", timer) return true end, @@ -540,11 +530,8 @@ local def_hopper_side = { sounds = mcl_sounds.node_sound_metal_defaults(), _mcl_minecarts_on_enter_below = function(pos, cart, next_dir) - print("Cart entered above "..tostring(pos)..",cart="..tostring(cart)) - -- Only push to containers if cart and cart.groups and (cart.groups.container or 0) ~= 0 then - print("Pulling from cart above "..tostring(pos)) cart:add_node_watch(pos) hopper_pull_from_mc(cart, pos, 5) end @@ -583,28 +570,17 @@ local def_hopper_side = { _mcl_minecarts_node_on_step = function(pos, cart, dtime, cartdata) if not cart then return end - local tick = false - local meta = minetest.get_meta(pos) - local timer = meta:get_int("minecart_hopper_timer") - print("dtime="..dtime..",timer="..timer) - if timer < dtime then - tick = true - timer = timer + 1 - else - timer = timer - dtime - end - meta:set_int("minecart_hopper_timer", timer) + if not mcl_util.metadata_timer(minetest.get_meta(pos), "minecart_hopper_timer", dtime) then return end - if tick then - local cart_pos = mcl_minecarts.get_cart_position(cartdata) - if not cart_pos then return false end + local cart_pos = mcl_minecarts.get_cart_position(cartdata) + if not cart_pos then return false end + if cart_pos.x ~= pos.x or cart_pos.z ~= pos.x then return end - print("uuid="..cartdata.uuid) - if cart_pos.y == pos.y then - hopper_push_to_mc(cart, pos, 5) - elseif cart_pos.y > pos.y then - hopper_pull_from_mc(cart, pos, 5) - end + print("uuid="..cartdata.uuid) + if cart_pos.y == pos.y then + hopper_push_to_mc(cart, pos, 5) + elseif cart_pos.y > pos.y then + hopper_pull_from_mc(cart, pos, 5) end return true From 7990ed65f6bf2df760a1245cb57d50cc70867b4f Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 14:44:55 +0000 Subject: [PATCH 101/195] Get rail placement creating corners that lead into a downward sloped rail --- mods/ENTITIES/mcl_minecarts/functions.lua | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 556af3dc5..7ad799339 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -177,7 +177,9 @@ local function is_connection(pos, dir) local get_next_dir = get_path(nodedef, "_mcl_minecarts", "get_next_dir") if not get_next_dir then return end - return get_next_dir(pos, dir, node) == dir + local next_dir = get_next_dir(pos, dir, node) + next_dir.y = 0 + return vector.equals(next_dir, dir) end local function get_rail_connections(pos, opt) @@ -188,14 +190,25 @@ local function get_rail_connections(pos, opt) for i = 1,#CONNECTIONS do dir = CONNECTIONS[i] local neighbor = vector.add(pos, dir) - local node = minetest.get_node(neighbor) + local node = force_get_node(neighbor) local nodedef = minetest.registered_nodes[node.name] -- Only allow connections to the open ends of rails, as decribed by get_next_dir - if get_path(nodedef, "groups", "rail") and ( legacy or get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) ) then - local rev_dir = vector.direction(dir,vector.new(0,0,0)) + if mcl_minecarts:is_rail(neighbor) and ( legacy or get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) ) then + local rev_dir = vector.direction(dir,vector.zero()) if ignore_neighbor_connections or is_connection(neighbor, rev_dir) then - connections = connections + bit.lshift(1,i - 1) + connections = bit.bor(connections, bit.lshift(1,i - 1)) + end + end + + -- Check for sloped rail one block down + local below_neighbor = vector.offset(neighbor, 0, -1, 0) + local node = force_get_node(below_neighbor) + local nodedef = minetest.registered_nodes[node.name] + if mcl_minecarts:is_rail(below_neighbor) and ( legacy or get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) ) then + local rev_dir = vector.direction(dir, vector.zero()) + if ignore_neighbor_connections or is_connection(below_neighbor, rev_dir) then + connections = bit.bor(connections, bit.lshift(1,i - 1)) end end end From 3bbd5a20fa6847fdb3d2fb96c8d5dccc9277dae7 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 18:17:17 +0000 Subject: [PATCH 102/195] More fixes for minecart-hopper movement --- mods/CORE/mcl_util/init.lua | 20 ++++++++-------- mods/ENTITIES/mcl_minecarts/movement.lua | 3 ++- mods/ITEMS/mcl_hoppers/init.lua | 29 +++++++++++++++++------- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 9daa885d9..6c14b3b1e 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -861,16 +861,16 @@ function mcl_util.assign_uuid(obj) return le._uuid end -function mcl_util.metadata_timer(meta, name, dtime) - local tick = false - local timer = meta:get_float(name) - if timer < dtime then - tick = true - timer = timer + 1 - else - timer = timer - dtime +function mcl_util.metadata_last_act(meta, name, delay) + local last_act = meta:get_float(name) + local now = minetest.get_us_time() * 1e-6 + if last_act > now + 0.5 then + -- Last action was in the future, clock went backwards, so reset + elseif last_act >= now - delay then + return false end - meta:set_float(name, timer) - if not tick then return true end + + meta:set_float(name, now) + return true end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index f750a4ad0..f1b405921 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -445,12 +445,13 @@ local function do_movement( staticdata, dtime ) assert(staticdata) -- Allow the carts to be delay for the rest of the world to react before moving again + --[[ if ( staticdata.delay or 0 ) > dtime then staticdata.delay = staticdata.delay - dtime return else staticdata.delay = 0 - end + end]] -- Break long movements at block boundaries to make it -- it impossible to jump across gaps due to server lag diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index cf39c760a..07dd52089 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -96,7 +96,12 @@ local function bent_hopper_act(pos, node, active_object_count, active_object_cou mcl_util.hopper_pull(pos, src_pos) end +--[[ + Returns true if an item was pushed to the minecart +]] local function hopper_push_to_mc(mc_ent, dest_pos, inv_size) + if not mcl_util.metadata_last_act(minetest.get_meta(dest_pos), "hopper_push_timer", 1) then return false end + local dest_inv = mcl_entity_invs.load_inv(mc_ent, inv_size) if not dest_inv then mcl_log("No inv") @@ -136,7 +141,12 @@ local function hopper_push_to_mc(mc_ent, dest_pos, inv_size) end end end +--[[ + Returns true if an item was pulled from the minecart +]] local function hopper_pull_from_mc(mc_ent, dest_pos, inv_size) + if not mcl_util.metadata_last_act(minetest.get_meta(dest_pos), "hopper_pull_timer", 1) then return false end + local inv = mcl_entity_invs.load_inv(mc_ent, inv_size) if not inv then mcl_log("No inv") @@ -309,12 +319,12 @@ local def_hopper = { return end - if not mcl_util.metadata_timer(minetest.get_meta(pos), "minecart_hopper_timer", dtime) then return end - local cart_pos = mcl_minecarts.get_cart_position(cartdata) if not cart_pos then return false end - if pos.x ~= cart_pos.x or pos.z ~= cart_pos.z then return end - + if vector.distance(cart_pos, pos) > 1.5 then + cart:remove_node_watch(pos) + return + end if vector.direction(pos,cart_pos).y > 0 then -- The cart is above us, pull from minecart hopper_pull_from_mc(cart, pos, 5) @@ -538,6 +548,7 @@ local def_hopper_side = { end, _mcl_minecarts_on_leave_below = function(pos, cart, next_dir) if not cart then return end + cart:remove_node_watch(pos) end, _mcl_minecarts_on_enter_side = function(pos, cart, next_dir, rail_pos) @@ -561,6 +572,8 @@ local def_hopper_side = { if cart.groups and (cart.groups.container or 0) ~= 0 then cart:add_node_watch(pos) end + + hopper_push_to_mc(cart, pos, 5) end, _mcl_minecarts_on_leave_side = function(pos, cart, next_dir) if not cart then return end @@ -570,13 +583,13 @@ local def_hopper_side = { _mcl_minecarts_node_on_step = function(pos, cart, dtime, cartdata) if not cart then return end - if not mcl_util.metadata_timer(minetest.get_meta(pos), "minecart_hopper_timer", dtime) then return end - local cart_pos = mcl_minecarts.get_cart_position(cartdata) if not cart_pos then return false end - if cart_pos.x ~= pos.x or cart_pos.z ~= pos.x then return end + if vector.distance(cart_pos, pos) > 1.5 then + cart:remove_node_watch(pos) + return false + end - print("uuid="..cartdata.uuid) if cart_pos.y == pos.y then hopper_push_to_mc(cart, pos, 5) elseif cart_pos.y > pos.y then From 22f5369db9964c3285fbe2d152a76a7ea0ceedaf Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 19:01:15 +0000 Subject: [PATCH 103/195] Add persistent player-specific metadata into mcl_playerinfo, simple cart reattachment (only exists if the luaentity for the cart exists when the player logs in) --- mods/ENTITIES/mcl_minecarts/carts.lua | 41 ++++++++++++ .../ENTITIES/mcl_minecarts/carts/minecart.lua | 67 ++++++++++--------- mods/PLAYER/mcl_playerinfo/init.lua | 27 +++++++- 3 files changed, 103 insertions(+), 32 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 26a95f826..d13059b99 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -42,6 +42,8 @@ local function detach_driver(self) -- Update cart informatino self._driver = nil self._start_pos = nil + local meta = mcl_playerinfo.get_mod_meta(driver_name, modname) + meta.attached_to = nil -- Detatch the player object from the minecart local player = minetest.get_player_by_name(driver_name) @@ -609,3 +611,42 @@ minetest.register_globalstep(function(dtime) end end) +minetest.register_on_joinplayer(function(player) + local player_name = player:get_player_name() + local meta = mcl_playerinfo.get_mod_meta(player_name, modname) + local cart_uuid = meta.attached_to + if cart_uuid then + print("Trying to reattach "..player_name.." to cart #"..cart_uuid) + local cartdata = get_cart_data(cart_uuid) + + -- Can't get into a cart that was destroyed + if not cartdata then + print("Failed to get cartdata") + return + end + + -- Don't reattach players if someone else got in the cart + if cartdata.last_player ~= player_name then + print("Somebody else got in the cart while you were gone") + return + end + + minetest.after(0.2,function(player_name, cart_uuid) + local player = minetest.get_player_by_name(player_name) + if not player then + print("Can't get an ObjectRef for "..player_name) + return + end + + local cart = mcl_util.get_luaentity_from_uuid(cart_uuid) + if not cart then + print("Can't find the luaentity for cart #"..cart_uuid) + return + end + + mod.attach_driver(cart, player) + + end, player_name, cart_uuid) + end +end) + diff --git a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua index bf3fce514..7f36fbb07 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua @@ -16,6 +16,41 @@ local function activate_normal_minecart(self) end end +function mod.attach_driver(cart, player) + local staticdata = cart._staticdata + + -- Make sure we have a player + if not player or not player:is_player() then return end + + local player_name = player:get_player_name() + if cart._driver or player:get_player_control().sneak then return end + + -- Update cart information + cart._driver = player_name + cart._start_pos = cart.object:get_pos() + + -- Keep track of player attachment + local meta = mcl_playerinfo.get_mod_meta(player_name, modname) + meta.attached_to = cart._uuid + staticdata.last_player = player_name + + -- Update player information + local uuid = staticdata.uuid + mcl_player.player_attached[player_name] = true + minetest.log("action", player_name.." entered minecart #"..tostring(uuid).." at "..tostring(cart._start_pos)) + + -- Attach the player object to the minecart + player:set_attach(cart.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) + minetest.after(0.2, function(name) + local player = minetest.get_player_by_name(name) + if player then + mcl_player.player_set_animation(player, "sit" , 30) + player:set_eye_offset(vector.new(0,-5.5,0), vector.new(0,-4,0)) + mcl_title.set(player, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) + end + end, player_name) +end + mod.register_minecart({ itemstring = "mcl_minecarts:minecart", craft = { @@ -39,37 +74,7 @@ mod.register_minecart({ }, icon = "mcl_minecarts_minecart_normal.png", drop = {"mcl_minecarts:minecart"}, - on_rightclick = function(self, clicker) - -- Make sure we have a player - if not clicker or not clicker:is_player() then return end - - local player_name = clicker:get_player_name() - if self._driver or clicker:get_player_control().sneak then return end - - -- Update cart information - self._driver = player_name - self._start_pos = self.object:get_pos() - - -- Update player information - local uuid = self._staticdata.uuid - local playerinfo = mcl_playerinfo[player_name] - if playerinfo and self._staticdata then - playerinfo.attached_to = uuid - end - mcl_player.player_attached[player_name] = true - minetest.log("action", player_name.." entered minecart #"..tostring(uuid).." at "..tostring(self._start_pos)) - - -- Attach the player object to the minecart - clicker:set_attach(self.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) - minetest.after(0.2, function(name) - local player = minetest.get_player_by_name(name) - if player then - mcl_player.player_set_animation(player, "sit" , 30) - player:set_eye_offset(vector.new(0,-5.5,0), vector.new(0,-4,0)) - mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60}) - end - end, player_name) - end, + on_rightclick = mod.attach_driver, on_activate_by_rail = activate_normal_minecart, _mcl_minecarts_on_step = function(self, dtime) -- Grab mob diff --git a/mods/PLAYER/mcl_playerinfo/init.lua b/mods/PLAYER/mcl_playerinfo/init.lua index f15f46b36..b2ce5cf70 100644 --- a/mods/PLAYER/mcl_playerinfo/init.lua +++ b/mods/PLAYER/mcl_playerinfo/init.lua @@ -1,7 +1,10 @@ local table = table +local storage = minetest.get_mod_storage() + -- Player state for public API mcl_playerinfo = {} +local player_mod_metadata = {} -- Get node but use fallback for nil or unknown local function node_ok(pos, fallback) @@ -73,6 +76,23 @@ minetest.register_globalstep(function(dtime) end) +function mcl_playerinfo.get_mod_meta(player_name, modname) + -- Load the player's metadata + local meta = player_mod_metadata[player_name] + if not meta then + meta = minetest.deserialize(storage:get_string(player_name)) + end + if not meta then + meta = {} + end + player_mod_metadata[player_name] = meta + + -- Get the requested module's section of the metadata + local mod_meta = meta[modname] or {} + meta[modname] = mod_meta + return mod_meta +end + -- set to blank on join (for 3rd party mods) minetest.register_on_joinplayer(function(player) local name = player:get_player_name() @@ -84,7 +104,6 @@ minetest.register_on_joinplayer(function(player) node_stand_below = "", node_head_top = "", } - end) -- clear when player leaves @@ -93,3 +112,9 @@ minetest.register_on_leaveplayer(function(player) mcl_playerinfo[name] = nil end) + +minetest.register_on_shutdown(function() + for name,data in pairs(player_mod_metadata) do + storage:set_string(name, minetest.serialize(data)) + end +end) From aec9665ecc491409f0db329855bfcc7291c01a17 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 19:04:53 +0000 Subject: [PATCH 104/195] Add documentation for newly exposed attach_driver --- mods/ENTITIES/mcl_minecarts/API.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md index ea1e13f1b..a315beb32 100644 --- a/mods/ENTITIES/mcl_minecarts/API.md +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -126,6 +126,10 @@ Called to get the next direction a cart will travel after passing thru this node ## Cart Functions +`mcl_minecarts.attach_driver(cart, player)` + +This attaches (ObjectRef) `player` to the (LuaEntity) `cart`. + `mcl_minecarts.detach_minecart(cart_data)` This detaches a minecart from any rail it is attached to and makes it start moving From 1831e34b098979fc8221063af107ca4e73094d51 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 19:09:23 +0000 Subject: [PATCH 105/195] Cleanup debug prints --- mods/ENTITIES/mcl_minecarts/carts.lua | 6 ------ mods/ENTITIES/mcl_minecarts/movement.lua | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index d13059b99..f6c23a6f5 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -616,36 +616,30 @@ minetest.register_on_joinplayer(function(player) local meta = mcl_playerinfo.get_mod_meta(player_name, modname) local cart_uuid = meta.attached_to if cart_uuid then - print("Trying to reattach "..player_name.." to cart #"..cart_uuid) local cartdata = get_cart_data(cart_uuid) -- Can't get into a cart that was destroyed if not cartdata then - print("Failed to get cartdata") return end -- Don't reattach players if someone else got in the cart if cartdata.last_player ~= player_name then - print("Somebody else got in the cart while you were gone") return end minetest.after(0.2,function(player_name, cart_uuid) local player = minetest.get_player_by_name(player_name) if not player then - print("Can't get an ObjectRef for "..player_name) return end local cart = mcl_util.get_luaentity_from_uuid(cart_uuid) if not cart then - print("Can't find the luaentity for cart #"..cart_uuid) return end mod.attach_driver(cart, player) - end, player_name, cart_uuid) end end) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index f1b405921..be9a0ca6c 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -33,7 +33,7 @@ mod.detach_minecart = detach_minecart local function try_detach_minecart(staticdata) if not staticdata or not staticdata.connected_at then return end if not mod:is_rail(staticdata.connected_at) then - print("Detaching minecart #"..tostring(staticdata.uuid)) + mcl_debug("Detaching minecart #"..tostring(staticdata.uuid)) detach_minecart(staticdata) end end From 4c56707336affef466cce704bf21df3a70c6e62e Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 20:47:47 +0000 Subject: [PATCH 106/195] Fix players repelling carts with new player metadata system --- mods/ENTITIES/mcl_minecarts/carts.lua | 12 ++++++------ mods/ENTITIES/mcl_minecarts/carts/minecart.lua | 4 ++-- mods/ENTITIES/mcl_minecarts/movement.lua | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index f6c23a6f5..9e6593d06 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -42,8 +42,8 @@ local function detach_driver(self) -- Update cart informatino self._driver = nil self._start_pos = nil - local meta = mcl_playerinfo.get_mod_meta(driver_name, modname) - meta.attached_to = nil + local player_meta = mcl_playerinfo.get_mod_meta(driver_name, modname) + player_meta.attached_to = nil -- Detatch the player object from the minecart local player = minetest.get_player_by_name(driver_name) @@ -267,7 +267,7 @@ function mod.kill_cart(staticdata, killer) if staticdata.attached_at then handle_cart_leave(self, staticdata.attached_at, staticdata.dir ) else - mcl_log("TODO: handle detatched minecart death") + --mcl_log("TODO: handle detatched minecart death") end -- Handle entity-related items @@ -527,7 +527,7 @@ local function respawn_cart(cart) end if not distance or distance > 90 then return end - print("Respawning cart #"..cart.uuid.." at "..tostring(pos)..",distance="..distance..",node="..minetest.get_node(pos).name) + mcl_log("Respawning cart #"..cart.uuid.." at "..tostring(pos)..",distance="..distance..",node="..minetest.get_node(pos).name) -- Update sequence so that old cart entities get removed cart.seq = (cart.seq or 1) + 1 @@ -613,8 +613,8 @@ end) minetest.register_on_joinplayer(function(player) local player_name = player:get_player_name() - local meta = mcl_playerinfo.get_mod_meta(player_name, modname) - local cart_uuid = meta.attached_to + local player_meta = mcl_playerinfo.get_mod_meta(player_name, modname) + local cart_uuid = player_meta.attached_to if cart_uuid then local cartdata = get_cart_data(cart_uuid) diff --git a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua index 7f36fbb07..693767b1d 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua @@ -30,8 +30,8 @@ function mod.attach_driver(cart, player) cart._start_pos = cart.object:get_pos() -- Keep track of player attachment - local meta = mcl_playerinfo.get_mod_meta(player_name, modname) - meta.attached_to = cart._uuid + local player_meta = mcl_playerinfo.get_mod_meta(player_name, modname) + player_meta.attached_to = cart._uuid staticdata.last_player = player_name -- Update player information diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index be9a0ca6c..2499bddc4 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -185,8 +185,8 @@ local function vector_away_from_players(cart, staticdata) if not player_name or player_name == "" then return false end -- Don't repel away from players in minecarts - local playerinfo = mcl_playerinfo[player_name] - if playerinfo and playerinfo.attached_to then return false end + local player_meta = mcl_playerinfo.get_mod_meta(player_name, modname) + if player_meta.attached_to then return false end return true end From de01ba45a6f8c921d1a337380a92dc5fd728024d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 12 Apr 2024 21:25:40 +0000 Subject: [PATCH 107/195] Fix placed rail conversion, start automatic inventory rail conversion --- mods/ENTITIES/mcl_minecarts/carts.lua | 3 ++- mods/ENTITIES/mcl_minecarts/rails.lua | 28 +++++++++++++++++++++++++-- mods/HUD/mcl_inventory/creative.lua | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 9e6593d06..1db3c0150 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -3,7 +3,7 @@ local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts local S = minetest.get_translator(modname) -local mcl_log = mcl_util.make_mcl_logger("mcl_logging_minecarts", "Minecarts") +local mcl_log,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecarts", "Minecarts") -- Imports local CART_BLOCK_SIZE = mod.CART_BLOCK_SIZE @@ -612,6 +612,7 @@ minetest.register_globalstep(function(dtime) end) minetest.register_on_joinplayer(function(player) + -- Try cart reattachment local player_name = player:get_player_name() local player_meta = mcl_playerinfo.get_mod_meta(player_name, modname) local cart_uuid = player_meta.attached_to diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index f09a702ad..fff59fe88 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -652,7 +652,10 @@ local CURVY_RAILS_MAP = { ["mcl_minecarts:rail"] = "mcl_minecarts:rail_v2", } for old,new in pairs(CURVY_RAILS_MAP) do - minetest.register_alias(old, new) + minetest.register_node(old, { + inventory_image = minetest.registered_nodes[new].inventory_image, + groups = { rail = 1 } + }) end minetest.register_lbm({ name = "mcl_minecarts:update_legacy_curvy_rails", @@ -674,8 +677,14 @@ local STRAIGHT_RAILS_MAP ={ ["mcl_minecarts:detector_rail_on"] = "mcl_minecarts:detector_rail_v2_on", } for old,new in pairs(STRAIGHT_RAILS_MAP) do - minetest.register_alias(old, new) + minetest.register_node(old, { + inventory_image = minetest.registered_nodes[new].inventory_image, + groups = { rail = 1 } + }) end +local TRANSLATE_RAILS_MAP = table.copy(STRAIGHT_RAILS_MAP) +table_merge(TRANSLATE_RAILS_MAP, CURVY_RAILS_MAP) + minetest.register_lbm({ name = "mcl_minecarts:update_legacy_straight_rails", nodenames = mcl_util.table_keys(STRAIGHT_RAILS_MAP), @@ -699,3 +708,18 @@ minetest.register_lbm({ end end }) + +-- Convert old rail in the player's inventory to new rail +minetest.register_on_joinplayer(function(player) + local inv = player:get_inventory() + local size = inv:get_size("main") + for i=1,size do + local stack = inv:get_stack("main", i) + + local new_name = TRANSLATE_RAILS_MAP[stack:get_name()] + if new_name then + stack:set_name(new_name) + inv:set_stack("main", i, stack) + end + end +end) diff --git a/mods/HUD/mcl_inventory/creative.lua b/mods/HUD/mcl_inventory/creative.lua index 3deade807..1f25932b2 100644 --- a/mods/HUD/mcl_inventory/creative.lua +++ b/mods/HUD/mcl_inventory/creative.lua @@ -408,7 +408,7 @@ local tab_icon = { blocks = "mcl_core:brick_block", deco = "mcl_flowers:peony", redstone = "mesecons:redstone", - rail = "mcl_minecarts:golden_rail", + rail = "mcl_minecarts:golden_rail_v2", misc = "mcl_buckets:bucket_lava", nix = "mcl_compass:compass", food = "mcl_core:apple", From 8351f39fb1ac48cd69a0d7d9b822583be0975fbb Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 16 Apr 2024 20:06:08 +0000 Subject: [PATCH 108/195] Fix crash after entering a minecart not on rails --- mods/ENTITIES/mcl_minecarts/carts.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 1db3c0150..6e4b9a528 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -248,7 +248,7 @@ function DEFAULT_CART_DEF:on_step(dtime) end -- Give achievement when player reached a distance of 1000 nodes from the start position - if vector.distance(self._start_pos, pos) >= 1000 then + if pos and vector.distance(self._start_pos, pos) >= 1000 then awards.unlock(self._driver, "mcl:onARail") end end From 689b6a8d3031b81592fb76c6c08981c73ba331fa Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 23 Apr 2024 16:34:39 +0000 Subject: [PATCH 109/195] Stop carts from reversing when they stop, make stopped carts try to start moving in the direction the player is facing --- mods/ENTITIES/mcl_minecarts/carts.lua | 2 ++ mods/ENTITIES/mcl_minecarts/movement.lua | 28 +++++++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 6e4b9a528..ed19690d7 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -21,6 +21,7 @@ assert(handle_cart_enter) -- Constants local max_step_distance = 0.5 local MINECART_MAX_HP = 4 +local TWO_OVER_PI = 2 / math.pi local function detach_driver(self) local staticdata = self._staticdata @@ -244,6 +245,7 @@ function DEFAULT_CART_DEF:on_step(dtime) local controls = {} if ctrl.up then controls.forward = now_time end if ctrl.down then controls.brake = now_time end + controls.look = math.round(player:get_look_horizontal() * TWO_OVER_PI) % 4 staticdata.controls = controls end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 2499bddc4..e4e682508 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -38,6 +38,16 @@ local function try_detach_minecart(staticdata) end end +local function reverse_direction(staticdata) + if staticdata.behind or staticdata.ahead then + reverse_train(staticdata) + return + end + + mod.reverse_cart_direction(staticdata) +end + + --[[ Array of hooks { {u,v,w}, name } Actual position is pos + u * dir + v * right + w * up @@ -248,6 +258,13 @@ local function calculate_acceleration(staticdata) local time_active = minetest.get_gametime() - 0.25 if (ctrl.forward or 0) > time_active then + if staticdata.velocity == 0 then + local look_dir = minetest.facedir_to_dir(ctrl.look or 0) + local dot = vector.dot(staticdata.dir, look_dir) + if dot < 0 then + reverse_direction(staticdata) + end + end acceleration = 4 elseif (ctrl.brake or 0) > time_active then acceleration = -1.5 @@ -270,15 +287,6 @@ local function calculate_acceleration(staticdata) return acceleration end -local function reverse_direction(staticdata) - if staticdata.behind or staticdata.ahead then - reverse_train(staticdata) - return - end - - mod.reverse_cart_direction(staticdata) -end - local function do_movement_step(staticdata, dtime) if not staticdata.connected_at then return 0 end @@ -405,7 +413,7 @@ local function do_movement_step(staticdata, dtime) -- Update cart direction staticdata.dir = next_dir - elseif stops_in_block and v_1 < (FRICTION/5) and a <= 0 then + elseif stops_in_block and v_1 < (FRICTION/5) and a <= 0 and staticdata.dir.y > 0 then -- Handle direction flip due to gravity if DEBUG then mcl_debug("Gravity flipped direction") end From 0a935d1f99234d42b379cc441d553910f2d3d00b Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 25 Apr 2024 07:16:31 +0000 Subject: [PATCH 110/195] Fix visual artifacts on the sides of rails --- mods/ENTITIES/mcl_minecarts/models/flat_track.obj | 15 +++++++++++++++ mods/ENTITIES/mcl_minecarts/rails.lua | 13 ++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 mods/ENTITIES/mcl_minecarts/models/flat_track.obj diff --git a/mods/ENTITIES/mcl_minecarts/models/flat_track.obj b/mods/ENTITIES/mcl_minecarts/models/flat_track.obj new file mode 100644 index 000000000..4df51c509 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/models/flat_track.obj @@ -0,0 +1,15 @@ +# hand-made Wavefront .OBJ file for sloped rail +mtllib mcl_minecarts_rail.mtl +o flat_track.001 +v -0.500000 -0.437500 -0.500000 +v -0.500000 -0.437500 0.500000 +v 0.500000 -0.437500 0.500000 +v 0.500000 -0.437500 -0.500000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vn 0.000000 1.000000 0.000000 +usemtl None +s off +f 1/1/1 2/2/1 3/3/1 4/4/1 diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index fff59fe88..8f7fc08b0 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -149,19 +149,18 @@ local railuse = S( mod.text = mod.text or {} mod.text.railuse = railuse local BASE_DEF = { - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - {-8/16, -8/16, -8/16, 8/16, -7/16, 8/15} - } - }, + drawtype = "mesh", + mesh = "flat_track.obj", paramtype = "light", paramtype2 = "4dir", stack_max = 64, sounds = mcl_sounds.node_sound_metal_defaults(), is_ground_content = true, paramtype = "light", + collision_box = { + type = "fixed", + fixed = { -8/16, -8/16, -8/16, 8/16, -7/16, 8/15 } + }, selection_box = { type = "fixed", fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, From 10865e6dbb48feaa048059c9c1cb0129544b2095 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 25 Apr 2024 08:34:11 +0000 Subject: [PATCH 111/195] Make punch move minecarts a little, comment out more debug prints --- mods/ENTITIES/mcl_minecarts/carts.lua | 25 ++++++++++++++++++++---- mods/ENTITIES/mcl_minecarts/movement.lua | 5 +++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index ed19690d7..287c00757 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -53,7 +53,7 @@ local function detach_driver(self) local cart_pos = mod.get_cart_position(staticdata) or self.object:get_pos() local new_pos = vector.offset(cart_pos, -dir.z, 0, dir.x) player:set_detach() - print("placing player at "..tostring(new_pos).." from cart at "..tostring(cart_pos)..", old_pos="..tostring(player:get_pos()).."dir="..tostring(dir)) + --print("placing player at "..tostring(new_pos).." from cart at "..tostring(cart_pos)..", old_pos="..tostring(player:get_pos()).."dir="..tostring(dir)) -- There needs to be a delay here or the player's position won't update minetest.after(0.1,function(driver_name,new_pos) @@ -63,8 +63,8 @@ local function detach_driver(self) player:set_eye_offset(vector.new(0,0,0),vector.new(0,0,0)) mcl_player.player_set_animation(player, "stand" , 30) - else - print("No player object found for "..driver_name) + --else + --print("No player object found for "..driver_name) end end @@ -188,6 +188,23 @@ function DEFAULT_CART_DEF:get_cart_position() return self.object:get_pos() end end +function DEFAULT_CART_DEF:on_punch(puncher, time_from_last_punch, tool_capabilities, dir, damage) + if puncher == self._driver then return end + + local staticdata = self._staticdata + local controls = staticdata.controls or {} + + local impulse = vector.multiply(dir, damage * 20) + + local accel = vector.dot(staticdata.dir, impulse) + if accel < 0 and staticdata.velocity == 0 then + mod.reverse_direction(staticdata) + end + + controls.impulse = impulse + --print("uuid="..self._uuid..", controls="..dump(controls)) + staticdata.controls = controls +end function DEFAULT_CART_DEF:on_step(dtime) local staticdata = self._staticdata if not staticdata then @@ -206,7 +223,7 @@ function DEFAULT_CART_DEF:on_step(dtime) -- Remove superceded entities if self._seq ~= staticdata.seq then - print("removing cart #"..staticdata.uuid.." with sequence number mismatch") + --print("removing cart #"..staticdata.uuid.." with sequence number mismatch") self.object:remove() return end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index e4e682508..bdefc0cab 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -46,6 +46,7 @@ local function reverse_direction(staticdata) mod.reverse_cart_direction(staticdata) end +mod.reverse_direction = reverse_direction --[[ @@ -268,6 +269,9 @@ local function calculate_acceleration(staticdata) acceleration = 4 elseif (ctrl.brake or 0) > time_active then acceleration = -1.5 + elseif ctrl.impulse then + acceleration = vector.dot(staticdata.dir, ctrl.impulse) + ctrl.impulse = nil elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then acceleration = 0.6 elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or SPEED_MAX ) then @@ -310,6 +314,7 @@ local function do_movement_step(staticdata, dtime) ": a="..tostring(a).. ",v_0="..tostring(v_0).. ",x_0="..tostring(x_0).. + ",dtime="..tostring(dtime).. ",timestep="..tostring(timestep).. ",dir="..tostring(staticdata.dir).. ",connected_at="..tostring(staticdata.connected_at).. From 356328ce4b7fc1344a445b78e97b5026a1b42a65 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 25 Apr 2024 15:38:08 +0000 Subject: [PATCH 112/195] Revert changed made to debug minecart-updates integration into tsm_railcorridors --- mods/MAPGEN/tsm_railcorridors/init.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mods/MAPGEN/tsm_railcorridors/init.lua b/mods/MAPGEN/tsm_railcorridors/init.lua index 1ff80fb4d..8f1de762c 100644 --- a/mods/MAPGEN/tsm_railcorridors/init.lua +++ b/mods/MAPGEN/tsm_railcorridors/init.lua @@ -1090,12 +1090,11 @@ mcl_structures.register_structure("mineshaft",{ y_min = mcl_vars.mg_overworld_min, place_func = function(pos,_,pr,blockseed) local r = pr:next(-50,-10) - r = -10 local p = vector.offset(pos,0,r,0) if p.y < mcl_vars.mg_overworld_min + 5 then p.y = mcl_vars.mg_overworld_min + 5 end - --if p.y > -10 then return true end + if p.y > -10 then return true end InitRandomizer(blockseed) local hook = tsm_railcorridors.on_start From fa29e3410b339962fe00104deef4a26cc959ca22 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 27 Apr 2024 18:12:06 +0000 Subject: [PATCH 113/195] Fix several undefined global warnings, fix cart movement when over maximum speed, fix cart reattachment to sloped track --- mods/CORE/mcl_util/init.lua | 2 +- mods/ENTITIES/mcl_minecarts/carts.lua | 17 +++------ mods/ENTITIES/mcl_minecarts/movement.lua | 44 ++++++++++++++++-------- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 6c14b3b1e..04ee46478 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -856,7 +856,7 @@ function mcl_util.assign_uuid(obj) end -- Update the cache with this new id - aoid = mcl_util.get_active_object_id(obj) + local aoid = mcl_util.get_active_object_id(obj) uuid_to_aoid_cache[le._uuid] = aoid return le._uuid diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 287c00757..e66176bc7 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -352,7 +352,7 @@ end function mod.create_minecart(entity_id, pos, dir) -- Setup cart data local uuid = mcl_util.gen_uuid() - data = make_staticdata( nil, pos, dir ) + local data = make_staticdata( nil, pos, dir ) data.uuid = uuid data.cart_type = entity_id update_cart_data(data) @@ -390,28 +390,21 @@ function mod.place_minecart(itemstack, pointed_thing, placer) -- Create the entity with the staticdata already setup local sd = minetest.serialize({ uuid=uuid, seq=1 }) local cart = minetest.add_entity(spawn_pos, entity_id, sd) + local staticdata = get_cart_data(uuid) cart:set_yaw(minetest.dir_to_yaw(cart_dir)) - -- Update static data - local le = cart:get_luaentity() - if le then - le._staticdata = data - end - -- Call placer + local le = cart:get_luaentity() if le._mcl_minecarts_on_place then le._mcl_minecarts_on_place(le, placer) end if railpos then - handle_cart_enter(data, railpos) + handle_cart_enter(staticdata, railpos) end - local pname = "" - if placer then - pname = placer:get_player_name() - end + local pname = placer and placer:get_player_name() or "" if not minetest.is_creative_enabled(pname) then itemstack:take_item() end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index bdefc0cab..44e752442 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -9,6 +9,7 @@ local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", " --mcl_debug = function(msg) print(msg) end -- Imports +local mcl_physics = mcl_physics or false local FRICTION = mcl_minecarts.FRICTION local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH local SPEED_MAX = mod.SPEED_MAX @@ -315,7 +316,6 @@ local function do_movement_step(staticdata, dtime) ",v_0="..tostring(v_0).. ",x_0="..tostring(x_0).. ",dtime="..tostring(dtime).. - ",timestep="..tostring(timestep).. ",dir="..tostring(staticdata.dir).. ",connected_at="..tostring(staticdata.connected_at).. ",distance="..tostring(staticdata.distance) @@ -347,7 +347,7 @@ local function do_movement_step(staticdata, dtime) -- Truncate timestep to prevent v_1 from being larger that speed_max local v_max = SPEED_MAX - if (v_0 ~= v_max) and ( v_0 + a * timestep > v_max) then + if (v_0 < v_max) and ( v_0 + a * timestep > v_max) then timestep = ( v_max - v_0 ) / a end @@ -519,22 +519,36 @@ local function do_detached_movement(self, dtime) end -- Try to reconnect to rail - local pos_r = vector.round(self.object:get_pos()) - local node = minetest.get_node(pos_r) - if minetest.get_item_group(node.name, "rail") ~= 0 then - staticdata.connected_at = pos_r - staticdata.railtype = node.name + local pos = self.object:get_pos() + local yaw = self.object:get_yaw() + local yaw_dir = minetest.yaw_to_dir(yaw) + local test_positions = { + pos, + vector.offset(vector.add(pos, vector.multiply(yaw_dir, 0.5)),0,-0.3,0), + vector.offset(vector.add(pos, vector.multiply(yaw_dir,-0.5)),0,-0.3,0), + } - local freebody_velocity = self.object:get_velocity() - staticdata.dir = mod:get_rail_direction(pos_r, mod.snap_direction(freebody_velocity)) + for i=1,#test_positions do + test_pos = test_positions[i] + local pos_r = vector.round(test_pos) + local node = minetest.get_node(pos_r) + if minetest.get_item_group(node.name, "rail") ~= 0 then + staticdata.connected_at = pos_r + staticdata.railtype = node.name - -- Use vector projection to only keep the velocity in the new direction of movement on the rail - -- https://en.wikipedia.org/wiki/Vector_projection - staticdata.velocity = vector.dot(staticdata.dir,freebody_velocity) + local freebody_velocity = self.object:get_velocity() + staticdata.dir = mod:get_rail_direction(pos_r, mod.snap_direction(freebody_velocity)) - -- Clear freebody movement - self.object:set_velocity(vector.new(0,0,0)) - self.object:set_acceleration(vector.new(0,0,0)) + -- Use vector projection to only keep the velocity in the new direction of movement on the rail + -- https://en.wikipedia.org/wiki/Vector_projection + staticdata.velocity = vector.dot(staticdata.dir,freebody_velocity) + print("Reattached velocity="..tostring(staticdata.velocity)..", freebody_velocity="..tostring(freebody_velocity)) + + -- Clear freebody movement + self.object:set_velocity(vector.new(0,0,0)) + self.object:set_acceleration(vector.new(0,0,0)) + return + end end end From c02ce8b8e70c1866cd1eed1785b7ad3132889885 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 27 Apr 2024 18:19:47 +0000 Subject: [PATCH 114/195] Fix typo, set use_texture_alpha = clip for all rail --- mods/ENTITIES/mcl_minecarts/functions.lua | 2 +- mods/ENTITIES/mcl_minecarts/rails.lua | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 7ad799339..7a44d036c 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -222,7 +222,7 @@ local function update_rail_connections(pos, opt) local node = minetest.get_node(pos) local nodedef = minetest.registered_nodes[node.name] if not nodedef or not nodedef._mcl_minecarts then - minetest.log("warning", "attemting to rail connect to "..node.name) + minetest.log("warning", "attempting to rail connect to "..node.name) return end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 8f7fc08b0..1107ce82a 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -157,6 +157,7 @@ local BASE_DEF = { sounds = mcl_sounds.node_sound_metal_defaults(), is_ground_content = true, paramtype = "light", + use_texture_alpha = "clip", collision_box = { type = "fixed", fixed = { -8/16, -8/16, -8/16, 8/16, -7/16, 8/15 } From 2c22f1537efcb0d805f58514796cf9b9ee52e5fd Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 27 Apr 2024 18:46:48 +0000 Subject: [PATCH 115/195] Fix cart detaching without unregistering from everything --- mods/ENTITIES/mcl_minecarts/movement.lua | 36 +++++++++++++----------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 44e752442..41e4a7a81 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -21,23 +21,6 @@ local update_cart_orientation = mod.update_cart_orientation local get_cart_data = mod.get_cart_data local get_cart_position = mod.get_cart_position -local function detach_minecart(staticdata) - staticdata.connected_at = nil - - local luaentity = mcl_util.get_luaentity_from_uuid(staticdata.uuid) - if luaentity then - luaentity.object:set_velocity(staticdata.dir * staticdata.velocity) - end -end -mod.detach_minecart = detach_minecart - -local function try_detach_minecart(staticdata) - if not staticdata or not staticdata.connected_at then return end - if not mod:is_rail(staticdata.connected_at) then - mcl_debug("Detaching minecart #"..tostring(staticdata.uuid)) - detach_minecart(staticdata) - end -end local function reverse_direction(staticdata) if staticdata.behind or staticdata.ahead then @@ -128,6 +111,25 @@ local function handle_cart_node_watches(staticdata, dtime) staticdata.node_watches = new_watches end +local function detach_minecart(staticdata) + handle_cart_leave(staticdata, staticdata.connected_at, staticdata.dir) + staticdata.connected_at = nil + + local luaentity = mcl_util.get_luaentity_from_uuid(staticdata.uuid) + if luaentity then + luaentity.object:set_velocity(staticdata.dir * staticdata.velocity) + end +end +mod.detach_minecart = detach_minecart + +local function try_detach_minecart(staticdata) + if not staticdata or not staticdata.connected_at then return end + if not mod:is_rail(staticdata.connected_at) then + mcl_debug("Detaching minecart #"..tostring(staticdata.uuid)) + detach_minecart(staticdata) + end +end + local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) if not cart1_staticdata then return end From 90cfa2cf4076ac53a7b31dda3ddc67fcf0ee5db9 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 28 Apr 2024 05:35:36 +0000 Subject: [PATCH 116/195] Change verticle offset for testing reattaching to rail to 0.55, which is a bit more than the stair step height --- mods/ENTITIES/mcl_minecarts/movement.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 41e4a7a81..06e67ed94 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -526,8 +526,8 @@ local function do_detached_movement(self, dtime) local yaw_dir = minetest.yaw_to_dir(yaw) local test_positions = { pos, - vector.offset(vector.add(pos, vector.multiply(yaw_dir, 0.5)),0,-0.3,0), - vector.offset(vector.add(pos, vector.multiply(yaw_dir,-0.5)),0,-0.3,0), + vector.offset(vector.add(pos, vector.multiply(yaw_dir, 0.5)),0,-0.55,0), + vector.offset(vector.add(pos, vector.multiply(yaw_dir,-0.5)),0,-0.55,0), } for i=1,#test_positions do From 8736f463ad1c8350fa304db21d4b6a9cd2fac0e8 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 28 Apr 2024 05:52:16 +0000 Subject: [PATCH 117/195] Convert curved rails direction code to use fourdir --- mods/ENTITIES/mcl_minecarts/rails.lua | 46 ++++++++++++++++----------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 1107ce82a..f54ccadf4 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -47,32 +47,42 @@ local function rail_dir_sloped(pos, dir, node) end local function rail_dir_curve(pos, dir, node) dir = vector.new(dir.x, 0, dir.z) + local dir_fourdir = minetest.dir_to_fourdir(dir) + -- Fourdir + -- 0 = north + -- 1 = east + -- 2 = south + -- 3 = west + + local new_fourdir = 0 if node.param2 == 0 then -- north -- South and East - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return east end - if vector.equals(dir, west) then return south end - if vector.equals(dir, east) then return east end + if dir_fourdir == 0 then new_fourdir = 1 end + if dir_fourdir == 1 then new_fourdir = 1 end + if dir_fourdir == 2 then new_fourdir = 2 end + if dir_fourdir == 3 then new_fourdir = 2 end elseif node.param2 == 1 then -- east -- South and West - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return west end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return south end - elseif node.param2 == 2 then + if dir_fourdir == 1 then new_fourdir = 2 end + if dir_fourdir == 2 then new_fourdir = 2 end + if dir_fourdir == 3 then new_fourdir = 3 end + if dir_fourdir == 0 then new_fourdir = 3 end + elseif node.param2 == 2 then -- south -- North and West - if vector.equals(dir, south) then return west end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return north end - elseif node.param2 == 3 then + if dir_fourdir == 2 then new_fourdir = 3 end + if dir_fourdir == 3 then new_fourdir = 3 end + if dir_fourdir == 0 then new_fourdir = 0 end + if dir_fourdir == 1 then new_fourdir = 0 end + elseif node.param2 == 3 then -- west -- North and East - if vector.equals(dir, south) then return east end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return north end - if vector.equals(dir, east) then return east end + if dir_fourdir == 3 then new_fourdir = 0 end + if dir_fourdir == 0 then new_fourdir = 0 end + if dir_fourdir == 1 then new_fourdir = 1 end + if dir_fourdir == 2 then new_fourdir = 1 end end + + return minetest.fourdir_to_dir(new_fourdir) end From 788db71850c9b45361152a2973dff827e394a0ed Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 28 Apr 2024 06:21:46 +0000 Subject: [PATCH 118/195] Rework rail_dir_curve to significantly reduce code size --- mods/ENTITIES/mcl_minecarts/rails.lua | 44 +++++---------------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index f54ccadf4..f4d14f867 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -45,47 +45,19 @@ local function rail_dir_sloped(pos, dir, node) return downhill end end +-- Fourdir +-- 0 = north +-- 1 = east +-- 2 = south +-- 3 = west +local CURVE_RAIL_DIRS = { [0] = 1, 1, 2, 2, } local function rail_dir_curve(pos, dir, node) dir = vector.new(dir.x, 0, dir.z) - local dir_fourdir = minetest.dir_to_fourdir(dir) - - -- Fourdir - -- 0 = north - -- 1 = east - -- 2 = south - -- 3 = west - - local new_fourdir = 0 - if node.param2 == 0 then -- north - -- South and East - if dir_fourdir == 0 then new_fourdir = 1 end - if dir_fourdir == 1 then new_fourdir = 1 end - if dir_fourdir == 2 then new_fourdir = 2 end - if dir_fourdir == 3 then new_fourdir = 2 end - elseif node.param2 == 1 then -- east - -- South and West - if dir_fourdir == 1 then new_fourdir = 2 end - if dir_fourdir == 2 then new_fourdir = 2 end - if dir_fourdir == 3 then new_fourdir = 3 end - if dir_fourdir == 0 then new_fourdir = 3 end - elseif node.param2 == 2 then -- south - -- North and West - if dir_fourdir == 2 then new_fourdir = 3 end - if dir_fourdir == 3 then new_fourdir = 3 end - if dir_fourdir == 0 then new_fourdir = 0 end - if dir_fourdir == 1 then new_fourdir = 0 end - elseif node.param2 == 3 then -- west - -- North and East - if dir_fourdir == 3 then new_fourdir = 0 end - if dir_fourdir == 0 then new_fourdir = 0 end - if dir_fourdir == 1 then new_fourdir = 1 end - if dir_fourdir == 2 then new_fourdir = 1 end - end - + local dir_fourdir = (minetest.dir_to_fourdir(dir) - node.param2 + 4) % 4 + local new_fourdir = (CURVE_RAIL_DIRS[dir_fourdir] + node.param2) % 4 return minetest.fourdir_to_dir(new_fourdir) end - local function rail_dir_tee_off(pos, dir, node) dir = vector.new(dir.x, 0, dir.z) From 3eb1e82f7e59e22ec2d0c9cb0ea8c7b026cb77d0 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 28 Apr 2024 06:58:42 +0000 Subject: [PATCH 119/195] Complete rework of curve/tee rail direction functions --- mods/ENTITIES/mcl_minecarts/rails.lua | 75 ++++++--------------------- 1 file changed, 17 insertions(+), 58 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index f4d14f867..8a6d1c8a4 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -45,77 +45,36 @@ local function rail_dir_sloped(pos, dir, node) return downhill end end --- Fourdir +-- Fourdir to cardinal direction -- 0 = north -- 1 = east -- 2 = south -- 3 = west -local CURVE_RAIL_DIRS = { [0] = 1, 1, 2, 2, } -local function rail_dir_curve(pos, dir, node) + +-- This takes a table `dirs` that has one element for each cardinal direction +-- and which specifies the direction for a cart to continue in when entering +-- a rail node in the direction of the cardinal. This function takes node +-- rotations into account. +local function rail_dir_from_table(pos, dir, node, dirs) dir = vector.new(dir.x, 0, dir.z) local dir_fourdir = (minetest.dir_to_fourdir(dir) - node.param2 + 4) % 4 - local new_fourdir = (CURVE_RAIL_DIRS[dir_fourdir] + node.param2) % 4 + local new_fourdir = (dirs[dir_fourdir] + node.param2) % 4 return minetest.fourdir_to_dir(new_fourdir) end +local CURVE_RAIL_DIRS = { [0] = 1, 1, 2, 2, } +local function rail_dir_curve(pos, dir, node) + return rail_dir_from_table(pos, dir, node, CURVE_RAIL_DIRS) +end local function rail_dir_tee_off(pos, dir, node) - dir = vector.new(dir.x, 0, dir.z) - - if node.param2 == 0 then -- north - -- South and East - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return east end - if vector.equals(dir, west) then return south end - if vector.equals(dir, east) then return east end - elseif node.param2 == 1 then -- east - -- South and West - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return west end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return south end - elseif node.param2 == 2 then - -- North and West - if vector.equals(dir, south) then return west end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return north end - elseif node.param2 == 3 then - -- North and East - if vector.equals(dir, south) then return east end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return north end - if vector.equals(dir, east) then return east end - end + return rail_dir_from_table(pos, dir, node, CURVE_RAIL_DIRS) end + +local TEE_RAIL_ON_DIRS = { [0] = 0, 1, 1, 0 } local function rail_dir_tee_on(pos, dir, node) - dir = vector.new(dir.x, 0, dir.z) - - if node.param2 == 0 then -- north - -- South and East - if vector.equals(dir, south) then return east end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return north end - if vector.equals(dir, east) then return east end - elseif node.param2 == 1 then -- east - -- South and West - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return east end - if vector.equals(dir, west) then return south end - if vector.equals(dir, east) then return east end - elseif node.param2 == 2 then - -- North and West - if vector.equals(dir, south) then return south end - if vector.equals(dir, north) then return west end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return south end - elseif node.param2 == 3 then - -- North and East - if vector.equals(dir, south) then return west end - if vector.equals(dir, north) then return north end - if vector.equals(dir, west) then return west end - if vector.equals(dir, east) then return north end - end + return rail_dir_from_table(pos, dir, node, TEE_RAIL_ON_DIRS) end + local function rail_dir_cross(pos, dir, node) dir = vector.new(dir.x, 0, dir.z) From 9c7b4d366c9a4b8d0ddaeeafb50d9cb015c141ab Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 28 Apr 2024 08:09:42 +0000 Subject: [PATCH 120/195] Restore 45 degree cart movement, remove warning about unknown global --- mods/ENTITIES/mcl_minecarts/functions.lua | 39 +++++++++++++++++++++-- mods/ENTITIES/mcl_minecarts/movement.lua | 2 +- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 7a44d036c..d9e12fb02 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -301,9 +301,8 @@ local function is_ahead_slope(pos, dir) local node_name = force_get_node(below).name return minetest.get_item_group(node_name, "rail_slope") ~= 0 end -function mcl_minecarts:get_rail_direction(pos_, dir) - local pos = vector.round(pos_) +local function get_rail_direction_inner(pos, dir) -- Handle new track types that have track-specific direction handler local node = minetest.get_node(pos) local node_def = minetest.registered_nodes[node.name] @@ -328,6 +327,42 @@ function mcl_minecarts:get_rail_direction(pos_, dir) return dir end +function mcl_minecarts:get_rail_direction(pos_, dir) + local pos = vector.round(pos_) + + -- diagonal direction handling + if dir.x ~= 0 and dir.z ~= 0 then + -- Check both possible diagonal movements + local dir_a = vector.new(dir.x,0,0) + local dir_b = vector.new(0,0,dir.z) + local new_dir_a = mcl_minecarts:get_rail_direction(pos, dir_a) + local new_dir_b = mcl_minecarts:get_rail_direction(pos, dir_b) + + -- If either is the same diagonal direction, continue as you were + if vector.equals(dir,new_dir_a) or vector.equals(dir,new_dir_b) then + return dir + + -- Otherwise, if either would try to move in the same direction as + -- what tried, move that direction + elseif vector.equals(dir_a, new_dir_a) then + return new_dir_a + elseif vector.equals(dir_b, new_dir_b) then + return new_dir_b + end + + -- And if none of these were true, fall thru into standard behavior + end + + local new_dir = get_rail_direction_inner(pos, dir) + + -- Check four 45 degree movement + local next_rails_dir = get_rail_direction_inner(vector.add(pos, new_dir), new_dir) + if vector.equals(next_rails_dir, dir) and not vector.equals(new_dir, next_rails_dir) then + return vector.add(new_dir, next_rails_dir) + else + return new_dir + end +end function mod.update_cart_orientation(self) local staticdata = self._staticdata diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 06e67ed94..522d533ab 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -531,7 +531,7 @@ local function do_detached_movement(self, dtime) } for i=1,#test_positions do - test_pos = test_positions[i] + local test_pos = test_positions[i] local pos_r = vector.round(test_pos) local node = minetest.get_node(pos_r) if minetest.get_item_group(node.name, "rail") ~= 0 then From f6a002a0b9ee72b9a06760985ffd1b7c533aa002 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 28 Apr 2024 20:17:33 +0000 Subject: [PATCH 121/195] Remove Emerge-0 warning that occurs when placing mineshafts --- mods/ENTITIES/mcl_minecarts/functions.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index d9e12fb02..9860327e8 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -221,10 +221,7 @@ local function update_rail_connections(pos, opt) local node = minetest.get_node(pos) local nodedef = minetest.registered_nodes[node.name] - if not nodedef or not nodedef._mcl_minecarts then - minetest.log("warning", "attempting to rail connect to "..node.name) - return - end + if not nodedef or not nodedef._mcl_minecarts then return end -- Get the mappings to use local rules = HORIZONTAL_RULES_BY_RAIL_GROUP[nodedef.groups.rail] From cd1e915638881dc02badd8146184ac8de9af035a Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 30 Apr 2024 00:15:06 +0000 Subject: [PATCH 122/195] Remove undefined global for optional environmental physics --- mods/ENTITIES/mcl_minecarts/movement.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 522d533ab..d9cbb7cb2 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -9,7 +9,10 @@ local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", " --mcl_debug = function(msg) print(msg) end -- Imports -local mcl_physics = mcl_physics or false +local env_physics +if minetest.get_modpath("mcl_physics") then + env_physics = mcl_physics +end local FRICTION = mcl_minecarts.FRICTION local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH local SPEED_MAX = mod.SPEED_MAX @@ -491,8 +494,8 @@ local function do_detached_movement(self, dtime) if not self.object or not self.object:get_pos() then return end -- Apply physics - if mcl_physics then - mcl_physics.apply_entity_environmental_physics(self) + if env_physics then + env_physics.apply_entity_environmental_physics(self) else -- Simple physics local friction = self.object:get_velocity() or vector.new(0,0,0) From dcb534bf9a293dab6ea53405ae973cc6d9026e58 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 4 May 2024 04:52:48 +0000 Subject: [PATCH 123/195] Move the various rails to their own files, code cleanup --- mods/ENTITIES/mcl_minecarts/carts.lua | 2 +- .../mcl_minecarts/carts/with_hopper.lua | 2 +- mods/ENTITIES/mcl_minecarts/functions.lua | 2 +- mods/ENTITIES/mcl_minecarts/mod.conf | 2 +- mods/ENTITIES/mcl_minecarts/movement.lua | 10 +- mods/ENTITIES/mcl_minecarts/rails.lua | 266 ++---------------- .../mcl_minecarts/rails/activator.lua | 87 ++++++ .../ENTITIES/mcl_minecarts/rails/detector.lua | 64 +++++ mods/ENTITIES/mcl_minecarts/rails/normal.lua | 27 ++ mods/ENTITIES/mcl_minecarts/rails/powered.lua | 69 +++++ 10 files changed, 281 insertions(+), 250 deletions(-) create mode 100644 mods/ENTITIES/mcl_minecarts/rails/activator.lua create mode 100644 mods/ENTITIES/mcl_minecarts/rails/detector.lua create mode 100644 mods/ENTITIES/mcl_minecarts/rails/normal.lua create mode 100644 mods/ENTITIES/mcl_minecarts/rails/powered.lua diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index e66176bc7..f6d221c58 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -61,7 +61,7 @@ local function detach_driver(self) player:moveto(new_pos, false) end, driver_name, new_pos) - player:set_eye_offset(vector.new(0,0,0),vector.new(0,0,0)) + player:set_eye_offset(vector.zero(),vector.zero()) mcl_player.player_set_animation(player, "stand" , 30) --else --print("No player object found for "..driver_name) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua b/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua index ac86cfe7f..022fc05f7 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua @@ -164,7 +164,7 @@ mod.register_minecart({ -- try to pull from containers into our inventory if not self then return end local inv = mcl_entity_invs.load_inv(self,5) - local above_pos = pos + vector.new(0,1,0) + local above_pos = vector.offset(pos,0,1,0) mcl_util.hopper_pull_to_inventory(inv, 'main', above_pos, pos) staticdata.hopper_delay = (staticdata.hopper_delay or 0) + (1/20) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 9860327e8..d476d606b 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -273,7 +273,7 @@ local function update_rail_connections(pos, opt) for i=1,#CONNECTIONS do local dir = CONNECTIONS[i] local higher_rail_pos = vector.offset(pos,dir.x,1,dir.z) - local rev_dir = vector.direction(dir,vector.new(0,0,0)) + local rev_dir = vector.direction(dir,vector.zero()) if mcl_minecarts:is_rail(higher_rail_pos) and is_connection(higher_rail_pos, rev_dir) then make_sloped_if_straight(pos, rev_dir) end diff --git a/mods/ENTITIES/mcl_minecarts/mod.conf b/mods/ENTITIES/mcl_minecarts/mod.conf index 29959b322..baa24737d 100644 --- a/mods/ENTITIES/mcl_minecarts/mod.conf +++ b/mods/ENTITIES/mcl_minecarts/mod.conf @@ -2,4 +2,4 @@ name = mcl_minecarts author = Krock description = Minecarts are vehicles to move players quickly on rails. depends = mcl_title, mcl_explosions, mcl_core, mcl_util, mcl_sounds, mcl_player, mcl_playerinfo, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs -optional_depends = doc_identifier, mcl_wip, mcl_physics +optional_depends = doc_identifier, mcl_wip, mcl_physics, vl_physics diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index d9cbb7cb2..74c8313ec 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -12,6 +12,8 @@ local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", " local env_physics if minetest.get_modpath("mcl_physics") then env_physics = mcl_physics +elseif minetest.get_modpath("vl_physics") then + env_physics = vl_physics end local FRICTION = mcl_minecarts.FRICTION local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH @@ -226,7 +228,7 @@ local function direction_away_from_players(staticdata) local diff = vector_away_from_players(nil,staticdata) if not diff then return 0 end - local length = vector.distance(vector.new(0,0,0),diff) + local length = vector.distance(vector.zero(),diff) local vec = diff / length local force = vector.dot( vec, vector.normalize(staticdata.dir) ) @@ -498,7 +500,7 @@ local function do_detached_movement(self, dtime) env_physics.apply_entity_environmental_physics(self) else -- Simple physics - local friction = self.object:get_velocity() or vector.new(0,0,0) + local friction = self.object:get_velocity() or vector.zero() friction.y = 0 local accel = vector.new(0,-9.81,0) -- gravity @@ -550,8 +552,8 @@ local function do_detached_movement(self, dtime) print("Reattached velocity="..tostring(staticdata.velocity)..", freebody_velocity="..tostring(freebody_velocity)) -- Clear freebody movement - self.object:set_velocity(vector.new(0,0,0)) - self.object:set_acceleration(vector.new(0,0,0)) + self.object:set_velocity(vector.zero()) + self.object:set_acceleration(vector.zero()) return end end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 8a6d1c8a4..85cc698f5 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -11,6 +11,10 @@ mod.RAIL_GROUPS = { local table_merge = mcl_util.table_merge local check_connection_rules = mod.check_connection_rules local update_rail_connections = mod.update_rail_connections +local minetest_fourdir_to_dir = minetest.fourdir_to_dir +local minetest_dir_to_fourdir = minetest.dir_to_fourdir +local vector_offset = vector.offset +local vector_equals = vector.equals local north = mod.north local south = mod.south local east = mod.east @@ -18,16 +22,17 @@ local west = mod.west --- Rail direction Handleres local function rail_dir_straight(pos, dir, node) - dir = vector.new(dir.x, 0, dir.z) + dir = vector.new(dir) + dir.y = 0 if node.param2 == 0 or node.param2 == 2 then - if vector.equals(dir, north) then + if vector_equals(dir, north) then return north else return south end else - if vector.equals(dir,east) then + if vector_equals(dir,east) then return east else return west @@ -35,11 +40,11 @@ local function rail_dir_straight(pos, dir, node) end end local function rail_dir_sloped(pos, dir, node) - local uphill = minetest.fourdir_to_dir(node.param2) - local downhill = minetest.fourdir_to_dir((node.param2+2)%4) - local up_uphill = vector.offset(uphill,0,1,0) + local uphill = minetest_fourdir_to_dir(node.param2) + local downhill = minetest_fourdir_to_dir((node.param2+2)%4) + local up_uphill = vector_offset(uphill,0,1,0) - if vector.equals(dir, uphill) or vector.equals(dir, up_uphill) then + if vector_equals(dir, uphill) or vector_equals(dir, up_uphill) then return up_uphill else return downhill @@ -56,10 +61,11 @@ end -- a rail node in the direction of the cardinal. This function takes node -- rotations into account. local function rail_dir_from_table(pos, dir, node, dirs) - dir = vector.new(dir.x, 0, dir.z) - local dir_fourdir = (minetest.dir_to_fourdir(dir) - node.param2 + 4) % 4 + dir = vector.new(dir) + dir.y = 0 + local dir_fourdir = (minetest_dir_to_fourdir(dir) - node.param2 + 4) % 4 local new_fourdir = (dirs[dir_fourdir] + node.param2) % 4 - return minetest.fourdir_to_dir(new_fourdir) + return minetest_fourdir_to_dir(new_fourdir) end local CURVE_RAIL_DIRS = { [0] = 1, 1, 2, 2, } @@ -76,7 +82,8 @@ local function rail_dir_tee_on(pos, dir, node) end local function rail_dir_cross(pos, dir, node) - dir = vector.new(dir.x, 0, dir.z) + dir = vector.new(dir) + dir.y = 0 -- Always continue in the same direction. No direction changes allowed return dir @@ -212,7 +219,6 @@ function mod.register_straight_rail(base_name, tiles, def) -- Sloped variant mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(sloped_def),{ - description = S("Sloped Rail"), -- Temporary name to make debugging easier _mcl_minecarts = { get_next_dir = rail_dir_sloped, }, @@ -268,9 +274,6 @@ function mod.register_curves_rail(base_name, tiles, def) -- Tee variants mod.register_rail(base_name.."_tee_off", table_merge(table.copy(base_def),{ tiles = { tiles[3] }, - groups = { - not_in_creative_inventory = 1, - }, mesecons = { effector = { action_on = function(pos, node) @@ -340,7 +343,7 @@ function mod.register_rail_sloped(itemstring, def) end -- Redstone rules -local rail_rules_long = +mod.rail_rules_long = {{x=-1, y= 0, z= 0, spread=true}, {x= 1, y= 0, z= 0, spread=true}, {x= 0, y=-1, z= 0, spread=true}, @@ -357,232 +360,10 @@ local rail_rules_long = {x= 0, y= 1, z=-1}, {x= 0, y=-1, z=-1}} -local rail_rules_short = mesecon.rules.pplate - --- Normal rail -mod.register_curves_rail("mcl_minecarts:rail_v2", { - "default_rail.png", - "default_rail_curved.png", - "default_rail_t_junction.png", - "default_rail_t_junction_on.png", - "default_rail_crossing.png" -},{ - description = S("Rail"), - _tt_help = S("Track for minecarts"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), - _doc_items_usagehelp = railuse, - craft = { - output = "mcl_minecarts:rail_v2 16", - recipe = { - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - } - }, -}) - --- Powered rail (off = brake mode) -mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_golden.png" },{ - description = S("Powered Rail"), - _tt_help = S("Track for minecarts").."\n"..S("Speed up when powered, slow down when not powered"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts."), - _doc_items_usagehelp = railuse .. "\n" .. S("Without redstone power, the rail will brake minecarts. To make this rail accelerate".. - " minecarts, power it with redstone power."), - _doc_items_create_entry = false, - _rail_acceleration = -3, - _max_acceleration_velocity = 8, - mesecons = { - conductor = { - state = mesecon.state.off, - offstate = "mcl_minecarts:golden_rail_v2", - onstate = "mcl_minecarts:golden_rail_v2_on", - rules = rail_rules_long, - }, - }, - mesecons_sloped = { - conductor = { - state = mesecon.state.off, - offstate = "mcl_minecarts:golden_rail_v2_sloepd", - onstate = "mcl_minecarts:golden_rail_v2_on_sloped", - rules = rail_rules_long, - }, - }, - drop = "mcl_minecarts:golden_rail_v2", - craft = { - output = "mcl_minecarts:golden_rail_v2 6", - recipe = { - {"mcl_core:gold_ingot", "", "mcl_core:gold_ingot"}, - {"mcl_core:gold_ingot", "mcl_core:stick", "mcl_core:gold_ingot"}, - {"mcl_core:gold_ingot", "mesecons:redstone", "mcl_core:gold_ingot"}, - } - } -}) - --- Powered rail (on = acceleration mode) -mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_rail_golden_powered.png" },{ - _doc_items_create_entry = false, - _rail_acceleration = 4, - _max_acceleration_velocity = 8, - groups = { - not_in_creative_inventory = 1, - }, - mesecons = { - conductor = { - state = mesecon.state.on, - offstate = "mcl_minecarts:golden_rail_v2", - onstate = "mcl_minecarts:golden_rail_v2_on", - rules = rail_rules_long, - }, - }, - mesecons_sloped = { - conductor = { - state = mesecon.state.on, - offstate = "mcl_minecarts:golden_rail_v2_sloped", - onstate = "mcl_minecarts:golden_rail_v2_on_sloped", - rules = rail_rules_long, - }, - }, - drop = "mcl_minecarts:golden_rail_v2", -}) - --- Activator rail (off) -mod.register_straight_rail("mcl_minecarts:activator_rail_v2", {"mcl_minecarts_rail_activator.png"},{ - description = S("Activator Rail"), - _tt_help = S("Track for minecarts").."\n"..S("Activates minecarts when powered"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts."), - _doc_items_usagehelp = railuse .. "\n" .. S("To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail."), - mesecons = { - conductor = { - state = mesecon.state.off, - offstate = "mcl_minecarts:activator_rail_v2", - onstate = "mcl_minecarts:activator_rail_v2_on", - rules = rail_rules_long, - }, - }, - mesecons_sloped = { - conductor = { - state = mesecon.state.off, - offstate = "mcl_minecarts:activator_rail_v2_sloped", - onstate = "mcl_minecarts:activator_rail_v2_on_sloped", - rules = rail_rules_long, - }, - }, - craft = { - output = "mcl_minecarts:activator_rail_v2 6", - recipe = { - {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mesecons_torch:mesecon_torch_on", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, - } - }, -}) - --- Activator rail (on) -local function activator_rail_action_on(pos, node) - local pos2 = { x = pos.x, y =pos.y + 1, z = pos.z } - local objs = minetest.get_objects_inside_radius(pos2, 1) - for _, o in pairs(objs) do - local l = o:get_luaentity() - if l and string.sub(l.name, 1, 14) == "mcl_minecarts:" and l.on_activate_by_rail then - l:on_activate_by_rail() - end - end -end -mod.register_straight_rail("mcl_minecarts:activator_rail_v2_on", {"mcl_minecarts_rail_activator_powered.png"},{ - _doc_items_create_entry = false, - groups = { - not_in_creative_inventory = 1, - }, - mesecons = { - conductor = { - state = mesecon.state.on, - offstate = "mcl_minecarts:activator_rail_v2", - onstate = "mcl_minecarts:activator_rail_v2_on", - rules = rail_rules_long, - }, - effector = { - -- Activate minecarts - action_on = activator_rail_action_on, - }, - - }, - mesecons_sloped = { - conductor = { - state = mesecon.state.on, - offstate = "mcl_minecarts:activator_rail_v2_sloped", - onstate = "mcl_minecarts:activator_rail_v2_on_sloped", - rules = rail_rules_long, - }, - effector = { - -- Activate minecarts - action_on = activator_rail_action_on, - }, - - }, - _mcl_minecarts_on_enter = function(pos, cart) - if cart.on_activate_by_rail then - cart:on_activate_by_rail() - end - end, - drop = "mcl_minecarts:activator_rail_v2", -}) - --- Detector rail (off) -mod.register_straight_rail("mcl_minecarts:detector_rail_v2",{"mcl_minecarts_rail_detector.png"},{ - description = S("Detector Rail"), - _tt_help = S("Track for minecarts").."\n"..S("Emits redstone power when a minecart is detected"), - _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms."), - _doc_items_usagehelp = railuse .. "\n" .. S("To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail."), - mesecons = { - receptor = { - state = mesecon.state.off, - rules = rail_rules_short, - }, - }, - _mcl_minecarts_on_enter = function(pos, cart) - local node = minetest.get_node(pos) - - local newnode = { - name = "mcl_minecarts:detector_rail_v2_on", - param2 = node.param2 - } - minetest.swap_node( pos, newnode ) - mesecon.receptor_on(pos) - end, - craft = { - output = "mcl_minecarts:detector_rail_v2 6", - recipe = { - {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mesecons_pressureplates:pressure_plate_stone_off", "mcl_core:iron_ingot"}, - {"mcl_core:iron_ingot", "mesecons:redstone", "mcl_core:iron_ingot"}, - } - } -}) - --- Detector rail (on) -mod.register_straight_rail("mcl_minecarts:detector_rail_v2_on",{"mcl_minecarts_rail_detector_powered.png"},{ - groups = { - not_in_creative_inventory = 1, - }, - _doc_items_create_entry = false, - mesecons = { - receptor = { - state = mesecon.state.on, - rules = rail_rules_short, - }, - }, - _mcl_minecarts_on_leave = function(pos, cart) - local node = minetest.get_node(pos) - - local newnode = { - name = "mcl_minecarts:detector_rail", - param2 = node.param2 - } - minetest.swap_node( pos, newnode ) - mesecon.receptor_off(pos) - end, - drop = "mcl_minecarts:detector_rail_v2", -}) +dofile(modpath.."/rails/normal.lua") +dofile(modpath.."/rails/activator.lua") +dofile(modpath.."/rails/detector.lua") +dofile(modpath.."/rails/powered.lua") -- Aliases if minetest.get_modpath("doc") then @@ -664,3 +445,4 @@ minetest.register_on_joinplayer(function(player) end end end) + diff --git a/mods/ENTITIES/mcl_minecarts/rails/activator.lua b/mods/ENTITIES/mcl_minecarts/rails/activator.lua new file mode 100644 index 000000000..8306dfa71 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/rails/activator.lua @@ -0,0 +1,87 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +-- Activator rail (off) +mod.register_straight_rail("mcl_minecarts:activator_rail_v2", {"mcl_minecarts_rail_activator.png"},{ + description = S("Activator Rail"), + _tt_help = S("Track for minecarts").."\n"..S("Activates minecarts when powered"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts."), + _doc_items_usagehelp = mod.text.railuse .. "\n" .. S("To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail."), + mesecons = { + conductor = { + state = mesecon.state.off, + offstate = "mcl_minecarts:activator_rail_v2", + onstate = "mcl_minecarts:activator_rail_v2_on", + rules = mod.rail_rules_long, + }, + }, + mesecons_sloped = { + conductor = { + state = mesecon.state.off, + offstate = "mcl_minecarts:activator_rail_v2_sloped", + onstate = "mcl_minecarts:activator_rail_v2_on_sloped", + rules = mod.rail_rules_long, + }, + }, + craft = { + output = "mcl_minecarts:activator_rail_v2 6", + recipe = { + {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mesecons_torch:mesecon_torch_on", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, + } + }, +}) + +-- Activator rail (on) +local function activator_rail_action_on(pos, node) + local pos2 = { x = pos.x, y =pos.y + 1, z = pos.z } + local objs = minetest.get_objects_inside_radius(pos2, 1) + for _, o in pairs(objs) do + local l = o:get_luaentity() + if l and string.sub(l.name, 1, 14) == "mcl_minecarts:" and l.on_activate_by_rail then + l:on_activate_by_rail() + end + end +end +mod.register_straight_rail("mcl_minecarts:activator_rail_v2_on", {"mcl_minecarts_rail_activator_powered.png"},{ + _doc_items_create_entry = false, + groups = { + not_in_creative_inventory = 1, + }, + mesecons = { + conductor = { + state = mesecon.state.on, + offstate = "mcl_minecarts:activator_rail_v2", + onstate = "mcl_minecarts:activator_rail_v2_on", + rules = mod.rail_rules_long, + }, + effector = { + -- Activate minecarts + action_on = activator_rail_action_on, + }, + + }, + mesecons_sloped = { + conductor = { + state = mesecon.state.on, + offstate = "mcl_minecarts:activator_rail_v2_sloped", + onstate = "mcl_minecarts:activator_rail_v2_on_sloped", + rules = mod.rail_rules_long, + }, + effector = { + -- Activate minecarts + action_on = activator_rail_action_on, + }, + + }, + _mcl_minecarts_on_enter = function(pos, cart) + if cart.on_activate_by_rail then + cart:on_activate_by_rail() + end + end, + drop = "mcl_minecarts:activator_rail_v2", +}) + diff --git a/mods/ENTITIES/mcl_minecarts/rails/detector.lua b/mods/ENTITIES/mcl_minecarts/rails/detector.lua new file mode 100644 index 000000000..b339c9335 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/rails/detector.lua @@ -0,0 +1,64 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +local rail_rules_short = mesecon.rules.pplate + +-- Detector rail (off) +mod.register_straight_rail("mcl_minecarts:detector_rail_v2",{"mcl_minecarts_rail_detector.png"},{ + description = S("Detector Rail"), + _tt_help = S("Track for minecarts").."\n"..S("Emits redstone power when a minecart is detected"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms."), + _doc_items_usagehelp = mod.text.railuse .. "\n" .. S("To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail."), + mesecons = { + receptor = { + state = mesecon.state.off, + rules = rail_rules_short, + }, + }, + _mcl_minecarts_on_enter = function(pos, cart) + local node = minetest.get_node(pos) + + local newnode = { + name = "mcl_minecarts:detector_rail_v2_on", + param2 = node.param2 + } + minetest.swap_node( pos, newnode ) + mesecon.receptor_on(pos) + end, + craft = { + output = "mcl_minecarts:detector_rail_v2 6", + recipe = { + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mesecons_pressureplates:pressure_plate_stone_off", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mesecons:redstone", "mcl_core:iron_ingot"}, + } + } +}) + +-- Detector rail (on) +mod.register_straight_rail("mcl_minecarts:detector_rail_v2_on",{"mcl_minecarts_rail_detector_powered.png"},{ + groups = { + not_in_creative_inventory = 1, + }, + _doc_items_create_entry = false, + mesecons = { + receptor = { + state = mesecon.state.on, + rules = rail_rules_short, + }, + }, + _mcl_minecarts_on_leave = function(pos, cart) + local node = minetest.get_node(pos) + + local newnode = { + name = "mcl_minecarts:detector_rail", + param2 = node.param2 + } + minetest.swap_node( pos, newnode ) + mesecon.receptor_off(pos) + end, + drop = "mcl_minecarts:detector_rail_v2", +}) + diff --git a/mods/ENTITIES/mcl_minecarts/rails/normal.lua b/mods/ENTITIES/mcl_minecarts/rails/normal.lua new file mode 100644 index 000000000..dc8293bb1 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/rails/normal.lua @@ -0,0 +1,27 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +-- Normal rail +mod.register_curves_rail("mcl_minecarts:rail_v2", { + "default_rail.png", + "default_rail_curved.png", + "default_rail_t_junction.png", + "default_rail_t_junction_on.png", + "default_rail_crossing.png" +},{ + description = S("Rail"), + _tt_help = S("Track for minecarts"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), + _doc_items_usagehelp = mod.text.railuse, + craft = { + output = "mcl_minecarts:rail_v2 16", + recipe = { + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"}, + } + }, +}) + diff --git a/mods/ENTITIES/mcl_minecarts/rails/powered.lua b/mods/ENTITIES/mcl_minecarts/rails/powered.lua new file mode 100644 index 000000000..dcad6a197 --- /dev/null +++ b/mods/ENTITIES/mcl_minecarts/rails/powered.lua @@ -0,0 +1,69 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local mod = mcl_minecarts +local S = minetest.get_translator(modname) + +-- Powered rail (off = brake mode) +mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_golden.png" },{ + description = S("Powered Rail"), + _tt_help = S("Track for minecarts").."\n"..S("Speed up when powered, slow down when not powered"), + _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts."), + _doc_items_usagehelp = mod.text.railuse .. "\n" .. S("Without redstone power, the rail will brake minecarts. To make this rail accelerate".. + " minecarts, power it with redstone power."), + _doc_items_create_entry = false, + _rail_acceleration = -3, + _max_acceleration_velocity = 8, + mesecons = { + conductor = { + state = mesecon.state.off, + offstate = "mcl_minecarts:golden_rail_v2", + onstate = "mcl_minecarts:golden_rail_v2_on", + rules = mod.rail_rules_long, + }, + }, + mesecons_sloped = { + conductor = { + state = mesecon.state.off, + offstate = "mcl_minecarts:golden_rail_v2_sloepd", + onstate = "mcl_minecarts:golden_rail_v2_on_sloped", + rules = mod.rail_rules_long, + }, + }, + drop = "mcl_minecarts:golden_rail_v2", + craft = { + output = "mcl_minecarts:golden_rail_v2 6", + recipe = { + {"mcl_core:gold_ingot", "", "mcl_core:gold_ingot"}, + {"mcl_core:gold_ingot", "mcl_core:stick", "mcl_core:gold_ingot"}, + {"mcl_core:gold_ingot", "mesecons:redstone", "mcl_core:gold_ingot"}, + } + } +}) + +-- Powered rail (on = acceleration mode) +mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_rail_golden_powered.png" },{ + _doc_items_create_entry = false, + _rail_acceleration = 4, + _max_acceleration_velocity = 8, + groups = { + not_in_creative_inventory = 1, + }, + mesecons = { + conductor = { + state = mesecon.state.on, + offstate = "mcl_minecarts:golden_rail_v2", + onstate = "mcl_minecarts:golden_rail_v2_on", + rules = mod.rail_rules_long, + }, + }, + mesecons_sloped = { + conductor = { + state = mesecon.state.on, + offstate = "mcl_minecarts:golden_rail_v2_sloped", + onstate = "mcl_minecarts:golden_rail_v2_on_sloped", + rules = mod.rail_rules_long, + }, + }, + drop = "mcl_minecarts:golden_rail_v2", +}) + From 41860500a31b06b742dc0f7f1e77d3e1354bb139 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 4 May 2024 06:33:41 +0000 Subject: [PATCH 124/195] Make old rails have a drawtype, make update lbm always run --- mods/ENTITIES/mcl_minecarts/rails.lua | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 85cc698f5..3aaf4b660 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -374,9 +374,12 @@ local CURVY_RAILS_MAP = { ["mcl_minecarts:rail"] = "mcl_minecarts:rail_v2", } for old,new in pairs(CURVY_RAILS_MAP) do + nodenames = mcl_util.table_keys(STRAIGHT_RAILS_MAP), minetest.register_node(old, { - inventory_image = minetest.registered_nodes[new].inventory_image, - groups = { rail = 1 } + drawtype = "raillike", + inventory_image = new_def.inventory_image, + groups = { rail = 1 }, + tiles = { new_def.tiles[1], new_def.tiles[1], new_def.tiles[1], new_def.tiles[1] }, }) end minetest.register_lbm({ @@ -399,9 +402,12 @@ local STRAIGHT_RAILS_MAP ={ ["mcl_minecarts:detector_rail_on"] = "mcl_minecarts:detector_rail_v2_on", } for old,new in pairs(STRAIGHT_RAILS_MAP) do + local new_def = minetest.registered_nodes[new] minetest.register_node(old, { - inventory_image = minetest.registered_nodes[new].inventory_image, - groups = { rail = 1 } + drawtype = "raillike", + inventory_image = new_def.inventory_image, + groups = { rail = 1 }, + tiles = { new_def.tiles[1], new_def.tiles[1], new_def.tiles[1], new_def.tiles[1] }, }) end local TRANSLATE_RAILS_MAP = table.copy(STRAIGHT_RAILS_MAP) @@ -410,6 +416,7 @@ table_merge(TRANSLATE_RAILS_MAP, CURVY_RAILS_MAP) minetest.register_lbm({ name = "mcl_minecarts:update_legacy_straight_rails", nodenames = mcl_util.table_keys(STRAIGHT_RAILS_MAP), + run_at_every_load = true, action = function(pos, node) node.name = STRAIGHT_RAILS_MAP[node.name] if node.name then From 78afadd8b16bdbd4ae99677ed6b7123d0ab888fb Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 18 Jun 2024 19:48:48 -0500 Subject: [PATCH 125/195] Fix crashes --- mods/ENTITIES/mcl_minecarts/rails.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 3aaf4b660..1cfd1b224 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -374,7 +374,7 @@ local CURVY_RAILS_MAP = { ["mcl_minecarts:rail"] = "mcl_minecarts:rail_v2", } for old,new in pairs(CURVY_RAILS_MAP) do - nodenames = mcl_util.table_keys(STRAIGHT_RAILS_MAP), + local new_def = minetest.registered_nodes[new] minetest.register_node(old, { drawtype = "raillike", inventory_image = new_def.inventory_image, From b47288a2603c411c86ebe85b3d6bffa542678abb Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 18 Jun 2024 20:21:12 -0500 Subject: [PATCH 126/195] Implement vl_legacy deprecated function and item conversion APIs --- mods/CORE/vl_legacy/API.md | 19 ++++++++++ mods/CORE/vl_legacy/init.lua | 52 ++++++++++++++++++++++++++ mods/CORE/vl_legacy/mod.conf | 3 ++ mods/ENTITIES/mcl_entity_invs/init.lua | 10 ++--- mods/ENTITIES/mcl_entity_invs/mod.conf | 2 +- 5 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 mods/CORE/vl_legacy/API.md create mode 100644 mods/CORE/vl_legacy/init.lua create mode 100644 mods/CORE/vl_legacy/mod.conf diff --git a/mods/CORE/vl_legacy/API.md b/mods/CORE/vl_legacy/API.md new file mode 100644 index 000000000..4bbc4658e --- /dev/null +++ b/mods/CORE/vl_legacy/API.md @@ -0,0 +1,19 @@ +# Legacy Code Support Functions + +## vl\_legacy.deprecated(description, replacement) + +Creates a wrapper than logs calls to deprecated function. + +Arguments: +* `description`: The text logged when the deprecated function is called. +* `replacement`: The function that should be called instead. This is invoked passing + along the parameters exactly as provided. + +## vl\_legacy.register\_item\_conversion + +Allows automatic conversion of items. + +Arguments: +* `old`: Itemstring to be converted +* `new`: New item string + diff --git a/mods/CORE/vl_legacy/init.lua b/mods/CORE/vl_legacy/init.lua new file mode 100644 index 000000000..7ac12067c --- /dev/null +++ b/mods/CORE/vl_legacy/init.lua @@ -0,0 +1,52 @@ +local mod = {} +vl_legacy = mod + +function mod.deprecated(description, func) + return function(...) + minetest.log("warning",description .. debug.traceback()) + return func(...) + end +end + +local item_conversions = {} +mod.registered_item_conversions = item_conversions + +function mod.register_item_conversion(old, new, func) + item_conversions[old] = {new, func} +end +function mod.convert_inventory_lists(lists) + for _,list in pairs(lists) do + for i = 1,#list do + local itemstack = list[i] + local conversion = item_conversions[itemstack:get_name()] + if conversion then + local new_name,func = conversion[1],conversion[2] + if func then + func(itemstack) + else + itemstack:set_name(new_name) + end + end + end + end +end +function mod.convert_inventory(inv) + local lists = inv:get_lists() + mod.convert_inventory_lists(lists) + inv:set_lists(lists) +end + +minetest.register_on_joinplayer(function(player) + mod.convert_inventory(player:get_inventory()) +end) + +minetest.register_lbm({ + name = "vl_legacy:convert_container_inventories", + nodenames = "group:containers", + run_at_every_load = true, + action = function(pos, node) + local meta = minetest.get_meta(pos) + mod.convert_inventory(meta:get_inventory()) + end +}) + diff --git a/mods/CORE/vl_legacy/mod.conf b/mods/CORE/vl_legacy/mod.conf new file mode 100644 index 000000000..11b6cd01b --- /dev/null +++ b/mods/CORE/vl_legacy/mod.conf @@ -0,0 +1,3 @@ +name = vl_legacy +author = teknomunk +description = API to ease conversion of items, deprecated function logging and similar functions diff --git a/mods/ENTITIES/mcl_entity_invs/init.lua b/mods/ENTITIES/mcl_entity_invs/init.lua index 601ec2019..ee9b50318 100644 --- a/mods/ENTITIES/mcl_entity_invs/init.lua +++ b/mods/ENTITIES/mcl_entity_invs/init.lua @@ -27,21 +27,19 @@ local inv_callbacks = { } function mcl_entity_invs.load_inv(ent,size) - mcl_log("load_inv") if not ent._inv_id then return end - mcl_log("load_inv 2") local inv = minetest.get_inventory({type="detached", name=ent._inv_id}) if not inv then - mcl_log("load_inv 3") inv = minetest.create_detached_inventory(ent._inv_id, inv_callbacks) inv:set_size("main", size) if ent._mcl_entity_invs_load_items then - inv:set_list("main",ent:_mcl_entity_invs_load_items()) + local lists = ent:_mcl_entity_invs_load_items() + vl_legacy.convert_inventory_lists(lists) + inv:set_list("main", lists) elseif ent._items then + vl_legacy.convert_inventory_lists(ent._items) inv:set_list("main",ent._items) end - else - mcl_log("load_inv 4") end return inv end diff --git a/mods/ENTITIES/mcl_entity_invs/mod.conf b/mods/ENTITIES/mcl_entity_invs/mod.conf index 8e94d6b1e..64f92aea9 100644 --- a/mods/ENTITIES/mcl_entity_invs/mod.conf +++ b/mods/ENTITIES/mcl_entity_invs/mod.conf @@ -1,3 +1,3 @@ name = mcl_entity_invs author = cora -depends = mcl_formspec +depends = mcl_formspec, vl_legacy From 0dece0afbfa28123539e6cef3f9a3dab7a895617 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 18 Jun 2024 20:21:34 -0500 Subject: [PATCH 127/195] Register rail conversions --- mods/ENTITIES/mcl_minecarts/mod.conf | 2 +- mods/ENTITIES/mcl_minecarts/rails.lua | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/mod.conf b/mods/ENTITIES/mcl_minecarts/mod.conf index baa24737d..df0f02bff 100644 --- a/mods/ENTITIES/mcl_minecarts/mod.conf +++ b/mods/ENTITIES/mcl_minecarts/mod.conf @@ -1,5 +1,5 @@ name = mcl_minecarts author = Krock description = Minecarts are vehicles to move players quickly on rails. -depends = mcl_title, mcl_explosions, mcl_core, mcl_util, mcl_sounds, mcl_player, mcl_playerinfo, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs +depends = mcl_title, mcl_explosions, mcl_core, mcl_util, mcl_sounds, mcl_player, mcl_playerinfo, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs, vl_legacy optional_depends = doc_identifier, mcl_wip, mcl_physics, vl_physics diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 1cfd1b224..6cdb18142 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -381,6 +381,7 @@ for old,new in pairs(CURVY_RAILS_MAP) do groups = { rail = 1 }, tiles = { new_def.tiles[1], new_def.tiles[1], new_def.tiles[1], new_def.tiles[1] }, }) + vl_legacy.register_item_conversion(old, new) end minetest.register_lbm({ name = "mcl_minecarts:update_legacy_curvy_rails", @@ -409,6 +410,7 @@ for old,new in pairs(STRAIGHT_RAILS_MAP) do groups = { rail = 1 }, tiles = { new_def.tiles[1], new_def.tiles[1], new_def.tiles[1], new_def.tiles[1] }, }) + vl_legacy.register_item_conversion(old, new) end local TRANSLATE_RAILS_MAP = table.copy(STRAIGHT_RAILS_MAP) table_merge(TRANSLATE_RAILS_MAP, CURVY_RAILS_MAP) From c165938c1b03113f4a6999ae89f30c1e87a21fe2 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 18 Jun 2024 20:29:56 -0500 Subject: [PATCH 128/195] Fix typo --- mods/CORE/vl_legacy/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/CORE/vl_legacy/init.lua b/mods/CORE/vl_legacy/init.lua index 7ac12067c..dd43a69c7 100644 --- a/mods/CORE/vl_legacy/init.lua +++ b/mods/CORE/vl_legacy/init.lua @@ -42,7 +42,7 @@ end) minetest.register_lbm({ name = "vl_legacy:convert_container_inventories", - nodenames = "group:containers", + nodenames = "group:container", run_at_every_load = true, action = function(pos, node) local meta = minetest.get_meta(pos) From 8c754bf55d649135d8abb78c0c14f27df330e57c Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 18 Jun 2024 20:31:27 -0500 Subject: [PATCH 129/195] Switch over to using vl_legacy for item conversion in player inventories --- mods/ENTITIES/mcl_minecarts/rails.lua | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 6cdb18142..b76b7bf03 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -412,8 +412,6 @@ for old,new in pairs(STRAIGHT_RAILS_MAP) do }) vl_legacy.register_item_conversion(old, new) end -local TRANSLATE_RAILS_MAP = table.copy(STRAIGHT_RAILS_MAP) -table_merge(TRANSLATE_RAILS_MAP, CURVY_RAILS_MAP) minetest.register_lbm({ name = "mcl_minecarts:update_legacy_straight_rails", @@ -440,18 +438,3 @@ minetest.register_lbm({ end }) --- Convert old rail in the player's inventory to new rail -minetest.register_on_joinplayer(function(player) - local inv = player:get_inventory() - local size = inv:get_size("main") - for i=1,size do - local stack = inv:get_stack("main", i) - - local new_name = TRANSLATE_RAILS_MAP[stack:get_name()] - if new_name then - stack:set_name(new_name) - inv:set_stack("main", i, stack) - end - end -end) - From a176d04bda72e759825e709a616e43f743c5fb64 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 6 Aug 2024 18:56:00 -0500 Subject: [PATCH 130/195] Correct documentation per review --- mods/CORE/vl_legacy/API.md | 6 +++--- mods/ENTITIES/mcl_minecarts/API.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mods/CORE/vl_legacy/API.md b/mods/CORE/vl_legacy/API.md index 4bbc4658e..7c1dde2e7 100644 --- a/mods/CORE/vl_legacy/API.md +++ b/mods/CORE/vl_legacy/API.md @@ -1,15 +1,15 @@ # Legacy Code Support Functions -## vl\_legacy.deprecated(description, replacement) +## `vl_legacy.deprecated(description, replacement)` -Creates a wrapper than logs calls to deprecated function. +Creates a wrapper that logs calls to deprecated function. Arguments: * `description`: The text logged when the deprecated function is called. * `replacement`: The function that should be called instead. This is invoked passing along the parameters exactly as provided. -## vl\_legacy.register\_item\_conversion +## `vl_legacy.register_item_conversion` Allows automatic conversion of items. diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md index a315beb32..e628e0d59 100644 --- a/mods/ENTITIES/mcl_minecarts/API.md +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -46,7 +46,7 @@ Where to attach passengers to the minecarts. `mcl_minecarts.HORIZONTAL_CURVES_RULES` `mcl_minecarts.HORIZONTAL_STANDARD_RULES` -Rail connection rules. Each rule is an table with the following indexes: +Rail connection rules. Each rule is a table with the following indexes: 1. `node_name_suffix` - The suffix added to a node's `_mcl_minecarts.base_name` to get the name of the node to use for this connection. @@ -167,7 +167,7 @@ Force a minecart to start moving in the opposite direction of its current direct Returns a valid cart movement direction that has the smallest angle between it and `direction_vector`. -`mcl_minecarts:update_cart_orientation(cart)` +`mcl_minecarts.update_cart_orientation(cart)` Updates the rotation of a cart entity to match the cart's data. From 16caed401cb198a960dd725986434670365e3c1a Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 6 Aug 2024 19:03:13 -0500 Subject: [PATCH 131/195] Rewrite mcl_util.hopper_pull in terms of mcl_util.hopper_pull_to_inventory --- mods/CORE/mcl_util/init.lua | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 04ee46478..119646eeb 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -241,7 +241,6 @@ function mcl_util.hopper_push(pos, dst_pos) end function mcl_util.hopper_pull_to_inventory(hop_inv, hop_list, src_pos, pos) - -- TODO: merge together with hopper_pull after https://git.minetest.land/MineClone2/MineClone2/pulls/4190 is merged -- Get node pos' for item transfer local src = minetest.get_node(src_pos) if not minetest.registered_nodes[src.name] then return end @@ -271,33 +270,7 @@ end ---@param pos Vector ---@param src_pos Vector function mcl_util.hopper_pull(pos, src_pos) - local hop_inv = minetest.get_meta(pos):get_inventory() - local hop_list = 'main' - - -- Get node pos' for item transfer - local src = minetest.get_node(src_pos) - if not minetest.registered_nodes[src.name] then return end - local src_type = minetest.get_item_group(src.name, "container") - if src_type ~= 2 then return end - local src_def = minetest.registered_nodes[src.name] - - local src_list = 'main' - local src_inv, stack_id - - if src_def._mcl_hoppers_on_try_pull then - src_inv, src_list, stack_id = src_def._mcl_hoppers_on_try_pull(src_pos, pos, hop_inv, hop_list) - else - local src_meta = minetest.get_meta(src_pos) - src_inv = src_meta:get_inventory() - stack_id = mcl_util.select_stack(src_inv, src_list, hop_inv, hop_list, nil, 1) - end - - if stack_id ~= nil then - local ok = mcl_util.move_item(src_inv, src_list, stack_id, hop_inv, hop_list) - if src_def._mcl_hoppers_on_after_pull then - src_def._mcl_hoppers_on_after_pull(src_pos) - end - end + return mcl_util.hopper_pull_to_inventory(minetest.get_meta(pos):get_inventory(), 'main', src_pos, pos) end local function drop_item_stack(pos, stack) From 80c0f52e179c4919ab31c43c9cb96314cba95c5a Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 7 Aug 2024 06:36:50 -0500 Subject: [PATCH 132/195] Update API documentation to always use , add compatibility shim to mcl_minecarts.is_rail() and mcl_minecarts.ge_rail_direction() --- mods/ENTITIES/mcl_minecarts/API.md | 4 ++-- mods/ENTITIES/mcl_minecarts/functions.lua | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/API.md b/mods/ENTITIES/mcl_minecarts/API.md index e628e0d59..1f90c32f4 100644 --- a/mods/ENTITIES/mcl_minecarts/API.md +++ b/mods/ENTITIES/mcl_minecarts/API.md @@ -77,7 +77,7 @@ Calculate the rail adjacency information for rail placement. Arguments are: - `ignore_neightbor_connections` - if true, don't check that a cart could leave the neighboring node from this direction. -`mcl_minecarts:is_rail(position, railtype)` +`mcl_minecarts.is_rail(position, railtype)` Determines if the node at `position` is a rail. If `railtype` is provided, determine if the node at `position` is that type of rail. @@ -101,7 +101,7 @@ Converts the rail at `node_position`, if possible, another variant (curve, etc.) and rotates the node as needed so that rails connect together. `options` is passed thru to `mcl_minecarts.get_rail_connections()` -`mcl_minecarts:get_rail_direction(rail_position, cart_direction)` +`mcl_minecarts.get_rail_direction(rail_position, cart_direction)` Returns the next direction a cart traveling in the direction specified in `cart_direction` will travel from the rail located at `rail_position`. diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index d476d606b..88a03a4c8 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -53,7 +53,13 @@ function mcl_minecarts:velocity_to_dir(v) end end -function mcl_minecarts:is_rail(pos, railtype) +function mcl_minecarts.is_rail(self, pos, railtype) + -- Compatibility with mcl_minecarts:is_rail() usage + if self ~= mcl_minecarts then + railtype = pos + pos = self + end + local node_name = force_get_node(pos).name if minetest.get_item_group(node_name, "rail") == 0 then @@ -324,7 +330,13 @@ local function get_rail_direction_inner(pos, dir) return dir end -function mcl_minecarts:get_rail_direction(pos_, dir) +function mcl_minecarts.get_rail_direction(self, pos_, dir) + -- Compatibility with mcl_minecarts:get_rail_direction() usage + if self ~= mcl_minecarts then + dir = pos_ + pos_ = self + end + local pos = vector.round(pos_) -- diagonal direction handling From 00665063da06e264a7f23c368db114cb796391b5 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 7 Aug 2024 21:25:38 -0500 Subject: [PATCH 133/195] Address additional review comments --- mods/CORE/mcl_util/init.lua | 2 +- mods/ENTITIES/mcl_minecarts/carts.lua | 8 ++++---- mods/ENTITIES/mcl_minecarts/functions.lua | 16 ++++++++-------- mods/ENTITIES/mcl_minecarts/movement.lua | 8 ++++---- mods/ENTITIES/mcl_minecarts/rails.lua | 1 + 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 119646eeb..fe195108b 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -270,7 +270,7 @@ end ---@param pos Vector ---@param src_pos Vector function mcl_util.hopper_pull(pos, src_pos) - return mcl_util.hopper_pull_to_inventory(minetest.get_meta(pos):get_inventory(), 'main', src_pos, pos) + return mcl_util.hopper_pull_to_inventory(minetest.get_meta(pos):get_inventory(), "main", src_pos, pos) end local function drop_item_stack(pos, stack) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index f6d221c58..c59bf6755 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -280,7 +280,7 @@ function DEFAULT_CART_DEF:on_step(dtime) end function mod.kill_cart(staticdata, killer) local pos - minetest.log("action", "cart #"..staticdata.uuid.." was killed") + mcl_log("cart #"..staticdata.uuid.." was killed") -- Leave nodes if staticdata.attached_at then @@ -372,15 +372,15 @@ function mod.place_minecart(itemstack, pointed_thing, placer) local cart_dir = vector.new(1,0,0) local railpos, node - if mcl_minecarts:is_rail(pointed_thing.under) then + if mcl_minecarts.is_rail(pointed_thing.under) then railpos = pointed_thing.under - elseif mcl_minecarts:is_rail(pointed_thing.above) then + elseif mcl_minecarts.is_rail(pointed_thing.above) then railpos = pointed_thing.above end if railpos then spawn_pos = railpos node = minetest.get_node(railpos) - cart_dir = mcl_minecarts:get_rail_direction(railpos, vector.new(1,0,0)) + cart_dir = mcl_minecarts.get_rail_direction(railpos, vector.new(1,0,0)) end local entity_id = entity_mapping[itemstack:get_name()] diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 88a03a4c8..d7cf8b99d 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -74,7 +74,7 @@ end -- Directional constants local north = vector.new( 0, 0, 1); local N = 1 -- 4dir = 0 local east = vector.new( 1, 0, 0); local E = 4 -- 4dir = 1 -local south = vector.new( 0, 0,-1); local S = 2 -- 4dir = 2 Note: S is overwritten below with the translator +local south = vector.new( 0, 0,-1); local S = 2 -- 4dir = 2 local west = vector.new(-1, 0, 0); local W = 8 -- 4dir = 3 -- Share. Consider moving this to some shared location @@ -200,7 +200,7 @@ local function get_rail_connections(pos, opt) local nodedef = minetest.registered_nodes[node.name] -- Only allow connections to the open ends of rails, as decribed by get_next_dir - if mcl_minecarts:is_rail(neighbor) and ( legacy or get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) ) then + if mcl_minecarts.is_rail(neighbor) and ( legacy or get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) ) then local rev_dir = vector.direction(dir,vector.zero()) if ignore_neighbor_connections or is_connection(neighbor, rev_dir) then connections = bit.bor(connections, bit.lshift(1,i - 1)) @@ -211,7 +211,7 @@ local function get_rail_connections(pos, opt) local below_neighbor = vector.offset(neighbor, 0, -1, 0) local node = force_get_node(below_neighbor) local nodedef = minetest.registered_nodes[node.name] - if mcl_minecarts:is_rail(below_neighbor) and ( legacy or get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) ) then + if mcl_minecarts.is_rail(below_neighbor) and ( legacy or get_path(nodedef, "_mcl_minecarts", "get_next_dir" ) ) then local rev_dir = vector.direction(dir, vector.zero()) if ignore_neighbor_connections or is_connection(below_neighbor, rev_dir) then connections = bit.bor(connections, bit.lshift(1,i - 1)) @@ -280,7 +280,7 @@ local function update_rail_connections(pos, opt) local dir = CONNECTIONS[i] local higher_rail_pos = vector.offset(pos,dir.x,1,dir.z) local rev_dir = vector.direction(dir,vector.zero()) - if mcl_minecarts:is_rail(higher_rail_pos) and is_connection(higher_rail_pos, rev_dir) then + if mcl_minecarts.is_rail(higher_rail_pos) and is_connection(higher_rail_pos, rev_dir) then make_sloped_if_straight(pos, rev_dir) end end @@ -296,10 +296,10 @@ local west = vector.new(-1,0,0) local function is_ahead_slope(pos, dir) local ahead = vector.add(pos,dir) - if mcl_minecarts:is_rail(ahead) then return false end + if mcl_minecarts.is_rail(ahead) then return false end local below = vector.offset(ahead,0,-1,0) - if not mcl_minecarts:is_rail(below) then return false end + if not mcl_minecarts.is_rail(below) then return false end local node_name = force_get_node(below).name return minetest.get_item_group(node_name, "rail_slope") ~= 0 @@ -344,8 +344,8 @@ function mcl_minecarts.get_rail_direction(self, pos_, dir) -- Check both possible diagonal movements local dir_a = vector.new(dir.x,0,0) local dir_b = vector.new(0,0,dir.z) - local new_dir_a = mcl_minecarts:get_rail_direction(pos, dir_a) - local new_dir_b = mcl_minecarts:get_rail_direction(pos, dir_b) + local new_dir_a = mcl_minecarts.get_rail_direction(pos, dir_a) + local new_dir_b = mcl_minecarts.get_rail_direction(pos, dir_b) -- If either is the same diagonal direction, continue as you were if vector.equals(dir,new_dir_a) or vector.equals(dir,new_dir_b) then diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 74c8313ec..0ad9e2331 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -179,7 +179,7 @@ local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) --print("u1="..tostring(u1)..",u2="..tostring(u2)) if u2 == 0 and u1 < 4 and train_length(cart1_staticdata) < MAX_TRAIN_LENGTH then link_cart_ahead(cart1_staticdata, cart2_staticdata) - cart2_staticdata.dir = mcl_minecarts:get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) + cart2_staticdata.dir = mcl_minecarts.get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) cart2_staticdata.velocity = cart1_staticdata.velocity return end @@ -194,7 +194,7 @@ local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) cart2_staticdata.velocity = v2 -- Force the other cart to move the same direction this one was - cart2_staticdata.dir = mcl_minecarts:get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) + cart2_staticdata.dir = mcl_minecarts.get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) end local function vector_away_from_players(cart, staticdata) @@ -238,7 +238,7 @@ local function direction_away_from_players(staticdata) if force > 0 then dir = -dir end - if mcl_minecarts:is_rail( staticdata.connected_at + dir ) then + if mcl_minecarts.is_rail( staticdata.connected_at + dir ) then if force > 0.5 then return -length * 4 elseif force < -0.5 then @@ -403,7 +403,7 @@ local function do_movement_step(staticdata, dtime) staticdata.connected_at = pos -- Get the next direction - local next_dir,_ = mcl_minecarts:get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) + local next_dir,_ = mcl_minecarts.get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) if DEBUG and next_dir ~= staticdata.dir then mcl_debug( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir)) end diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index b76b7bf03..bb76cdc36 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -386,6 +386,7 @@ end minetest.register_lbm({ name = "mcl_minecarts:update_legacy_curvy_rails", nodenames = mcl_util.table_keys(CURVY_RAILS_MAP), + run_at_every_load = true, action = function(pos, node) node.name = CURVY_RAILS_MAP[node.name] if node.name then From c328f9920e7a90e014b9bf9f9d3d6b849c202452 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 7 Aug 2024 21:32:45 -0500 Subject: [PATCH 134/195] Add profiling code to force_get_node() --- mods/ENTITIES/mcl_minecarts/functions.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index d7cf8b99d..abef2b643 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -11,6 +11,7 @@ local function force_get_node(pos) local node = minetest.get_node(pos) if node.name ~= "ignore" then return node end + local time_start = minetest.get_us_time() local vm = minetest.get_voxel_manip() local emin, emax = vm:read_from_map(pos, pos) local area = VoxelArea:new{ @@ -22,11 +23,13 @@ local function force_get_node(pos) local param2_data = vm:get_param2_data() local vi = area:indexp(pos) + minetest.log("force_get_node() voxel_manip section took "..((minetest.get_us_time()-time_start)*1e-6).." seconds") return { name = minetest.get_name_from_content_id(data[vi]), param = param_data[vi], param2 = param2_data[vi] } + end function mcl_minecarts:get_sign(z) From b83fb8ccbad21ff88ff6d81b7983a9c4cfb330d7 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 8 Aug 2024 07:01:26 -0500 Subject: [PATCH 135/195] Expand mcl_util.hopper_pull() to mcl_util.hopper_pull_to_inventory() --- mods/ITEMS/mcl_hoppers/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index 07dd52089..995703e40 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -54,7 +54,7 @@ local function straight_hopper_act(pos, node, active_object_count, active_count_ mcl_util.hopper_push(pos, dst_pos) local src_pos = vector.offset(pos, 0, 1, 0) - mcl_util.hopper_pull(pos, src_pos) + mcl_util.hopper_pull_to_inventory(minetest.get_meta(pos):get_inventory(), "main", src_pos, pos) end local function bent_hopper_act(pos, node, active_object_count, active_object_count_wider) @@ -93,7 +93,7 @@ local function bent_hopper_act(pos, node, active_object_count, active_object_cou end local src_pos = vector.offset(pos, 0, 1, 0) - mcl_util.hopper_pull(pos, src_pos) + mcl_util.hopper_pull_to_inventory(inv, "main", src_pos, pos) end --[[ From 3d7f45154bcdba80b8f498d90587ab648c2ef0fe Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 8 Aug 2024 07:21:54 -0500 Subject: [PATCH 136/195] Fix a couple of crashes (TNT minecart trying to update orientation after exploding, trying to punch/push a minecart not on track) --- mods/ENTITIES/mcl_minecarts/functions.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index abef2b643..187fa985f 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -391,6 +391,8 @@ function mod.update_cart_orientation(self) -- Check if the rotation is a 180 flip and don't change if so local rot = self.object:get_rotation() + if not rot then return end + local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) if diff < 0.001 or diff > _2_pi - 0.001 then -- Update rotation adjust and recalculate the rotation @@ -431,6 +433,8 @@ function mod.reverse_cart_direction(staticdata) -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at local next_dir = -staticdata.dir + if not staticdata.connected_at then return end + staticdata.connected_at = staticdata.connected_at + staticdata.dir staticdata.distance = 1 - (staticdata.distance or 0) From 46c1330d312a9996e8bccb09d34e920157cc61d4 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 10 Aug 2024 20:56:34 -0500 Subject: [PATCH 137/195] Make activated tnt minecarts glow in the dark, fix crash with lit tnt minecarts --- .../ENTITIES/mcl_minecarts/carts/with_tnt.lua | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua b/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua index 82bc1718e..8b0c7d869 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/with_tnt.lua @@ -18,15 +18,18 @@ local function activate_tnt_minecart(self, timer) else self._boomtimer = tnt.BOOMTIMER end - self.object:set_properties({textures = { - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_tnt_blink.png", - "mcl_minecarts_minecart.png", - }}) + self.object:set_properties({ + textures = { + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_tnt_blink.png", + "mcl_minecarts_minecart.png", + }, + glow = 15, + }) self._blinktimer = tnt.BLINKTIMER minetest.sound_play("tnt_ignite", {pos = self.object:get_pos(), gain = 1.0, max_hear_distance = 15}, true) end @@ -95,7 +98,10 @@ mod.register_minecart({ detonate_tnt_minecart(self) return else - tnt.smoke_step(pos) + local pos = mod.get_cart_position(self._staticdata) or self.object:get_pos() + if pos then + tnt.smoke_step(pos) + end end end From b963936c6d4671d899c9c50aac0c176f57280958 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 11 Aug 2024 10:39:03 -0500 Subject: [PATCH 138/195] Add legacy node conversion to vl_legacy and update rails.lua to use it --- mods/CORE/vl_legacy/API.md | 16 +++++++ mods/CORE/vl_legacy/init.lua | 25 +++++++++- mods/ENTITIES/mcl_minecarts/rails.lua | 69 ++++++++++++--------------- 3 files changed, 70 insertions(+), 40 deletions(-) diff --git a/mods/CORE/vl_legacy/API.md b/mods/CORE/vl_legacy/API.md index 7c1dde2e7..ceb11aad1 100644 --- a/mods/CORE/vl_legacy/API.md +++ b/mods/CORE/vl_legacy/API.md @@ -17,3 +17,19 @@ Arguments: * `old`: Itemstring to be converted * `new`: New item string +## `vl_legacy.convert_node(pos, node)` + +Converts legacy nodes to newer versions. + +Arguments: +* `pos`: Position of the node to attempt conversion +* `node`: Node definition to convert. The node will be loaded from map data if `nil`. + +The node definition for the old node must contain the field `_vl_legacy_convert` with +a value that is either a `function(pos, node)` or `string` for this call to have any +affect. If a function is provided, the function is called with `pos` and `node` as +arguments. If a string is provided, a node name conversion will occur. + +This mod provides an LBM and ABM that will automatically call this function for nodes +with `group:legacy` set. + diff --git a/mods/CORE/vl_legacy/init.lua b/mods/CORE/vl_legacy/init.lua index dd43a69c7..ca89f799c 100644 --- a/mods/CORE/vl_legacy/init.lua +++ b/mods/CORE/vl_legacy/init.lua @@ -35,6 +35,17 @@ function mod.convert_inventory(inv) mod.convert_inventory_lists(lists) inv:set_lists(lists) end +function mod.convert_node(pos, node) + local node = node or minetest.get_node(pos) + local node_def = minetest.registered_nodes[node.name] + local convert = node_def._vl_legacy_convert_node + if type(convert) == "function" then + convert(pos, node) + elseif type(convert) == "string" then + node.name = convert + minetest.swap_node(pos, node) + end +end minetest.register_on_joinplayer(function(player) mod.convert_inventory(player:get_inventory()) @@ -49,4 +60,16 @@ minetest.register_lbm({ mod.convert_inventory(meta:get_inventory()) end }) - +minetest.register_lbm({ + name = "vl_legacy:convert_nodes", + nodenames = "group:legacy", + run_at_every_load = true, + action = mod.convert_node, +}) +minetest.register_abm({ + label = "Convert Legacy Nodes", + nodenames = "group:legacy", + interval = 5, + chance = 1, + action = mod.convert_node, +}) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index bb76cdc36..00c78616d 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -373,28 +373,24 @@ end local CURVY_RAILS_MAP = { ["mcl_minecarts:rail"] = "mcl_minecarts:rail_v2", } +local function convert_legacy_curvy_rails(pos, node) + node.name = CURVY_RAILS_MAP[node.name] + if node.name then + minetest.swap_node(pos, node) + mod.update_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true }) + end +end for old,new in pairs(CURVY_RAILS_MAP) do local new_def = minetest.registered_nodes[new] minetest.register_node(old, { drawtype = "raillike", inventory_image = new_def.inventory_image, - groups = { rail = 1 }, + groups = { rail = 1, legacy = 1 }, tiles = { new_def.tiles[1], new_def.tiles[1], new_def.tiles[1], new_def.tiles[1] }, + _vl_legacy_convert_node = convert_legacy_curvy_rails }) vl_legacy.register_item_conversion(old, new) end -minetest.register_lbm({ - name = "mcl_minecarts:update_legacy_curvy_rails", - nodenames = mcl_util.table_keys(CURVY_RAILS_MAP), - run_at_every_load = true, - action = function(pos, node) - node.name = CURVY_RAILS_MAP[node.name] - if node.name then - minetest.swap_node(pos, node) - mod.update_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true }) - end - end -}) local STRAIGHT_RAILS_MAP ={ ["mcl_minecarts:golden_rail"] = "mcl_minecarts:golden_rail_v2", ["mcl_minecarts:golden_rail_on"] = "mcl_minecarts:golden_rail_v2_on", @@ -403,39 +399,34 @@ local STRAIGHT_RAILS_MAP ={ ["mcl_minecarts:detector_rail"] = "mcl_minecarts:detector_rail_v2", ["mcl_minecarts:detector_rail_on"] = "mcl_minecarts:detector_rail_v2_on", } +local function convert_legacy_straight_rail(pos, node) + node.name = STRAIGHT_RAILS_MAP[node.name] + if node.name then + local connections = mod.get_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true }) + if not mod.HORIZONTAL_STANDARD_RULES[connections] then + -- Drop an immortal object at this location + local item_entity = minetest.add_item(pos, ItemStack(node.name)) + if item_entity then + item_entity:get_luaentity()._immortal = true + end + + -- This is a configuration that doesn't exist in the new rail + -- Replace with a standard rail + node.name = "mcl_minecarts:rail_v2" + end + minetest.swap_node(pos, node) + mod.update_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true }) + end +end for old,new in pairs(STRAIGHT_RAILS_MAP) do local new_def = minetest.registered_nodes[new] minetest.register_node(old, { drawtype = "raillike", inventory_image = new_def.inventory_image, - groups = { rail = 1 }, + groups = { rail = 1, legacy = 1 }, tiles = { new_def.tiles[1], new_def.tiles[1], new_def.tiles[1], new_def.tiles[1] }, + _vl_legacy_convert_node = convert_legacy_straight_rail, }) vl_legacy.register_item_conversion(old, new) end -minetest.register_lbm({ - name = "mcl_minecarts:update_legacy_straight_rails", - nodenames = mcl_util.table_keys(STRAIGHT_RAILS_MAP), - run_at_every_load = true, - action = function(pos, node) - node.name = STRAIGHT_RAILS_MAP[node.name] - if node.name then - local connections = mod.get_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true }) - if not mod.HORIZONTAL_STANDARD_RULES[connections] then - -- Drop an immortal object at this location - local item_entity = minetest.add_item(pos, ItemStack(node.name)) - if item_entity then - item_entity:get_luaentity()._immortal = true - end - - -- This is a configuration that doesn't exist in the new rail - -- Replace with a standard rail - node.name = "mcl_minecarts:rail_v2" - end - minetest.swap_node(pos, node) - mod.update_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true }) - end - end -}) - From 8cde9d4244dec9035ea4b032a9ce26f9bfc096ac Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 31 Aug 2024 16:36:01 -0500 Subject: [PATCH 139/195] Prevent removal of old minecarts --- mods/ENTITIES/mcl_minecarts/carts.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index c59bf6755..d730078f7 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -222,7 +222,7 @@ function DEFAULT_CART_DEF:on_step(dtime) end -- Remove superceded entities - if self._seq ~= staticdata.seq then + if staticdata.seq and self._seq < staticdata.seq then --print("removing cart #"..staticdata.uuid.." with sequence number mismatch") self.object:remove() return From ee49ff612dfecb8ba3fa56c992718318c60f37a1 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 31 Aug 2024 16:39:59 -0500 Subject: [PATCH 140/195] Silence debug prints and logging --- mods/ENTITIES/mcl_minecarts/carts/minecart.lua | 2 +- mods/ENTITIES/mcl_minecarts/functions.lua | 4 ++-- mods/ENTITIES/mcl_minecarts/movement.lua | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua index 693767b1d..b736a4c03 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua @@ -37,7 +37,7 @@ function mod.attach_driver(cart, player) -- Update player information local uuid = staticdata.uuid mcl_player.player_attached[player_name] = true - minetest.log("action", player_name.." entered minecart #"..tostring(uuid).." at "..tostring(cart._start_pos)) + --minetest.log("action", player_name.." entered minecart #"..tostring(uuid).." at "..tostring(cart._start_pos)) -- Attach the player object to the minecart player:set_attach(cart.object, "", vector.new(1,-1.75,-2), vector.new(0,0,0)) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 187fa985f..caac7daf8 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -11,7 +11,7 @@ local function force_get_node(pos) local node = minetest.get_node(pos) if node.name ~= "ignore" then return node end - local time_start = minetest.get_us_time() + --local time_start = minetest.get_us_time() local vm = minetest.get_voxel_manip() local emin, emax = vm:read_from_map(pos, pos) local area = VoxelArea:new{ @@ -23,7 +23,7 @@ local function force_get_node(pos) local param2_data = vm:get_param2_data() local vi = area:indexp(pos) - minetest.log("force_get_node() voxel_manip section took "..((minetest.get_us_time()-time_start)*1e-6).." seconds") + --minetest.log("force_get_node() voxel_manip section took "..((minetest.get_us_time()-time_start)*1e-6).." seconds") return { name = minetest.get_name_from_content_id(data[vi]), param = param_data[vi], diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 0ad9e2331..b60fbfe25 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -166,7 +166,7 @@ local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) -- Don't collide with the train car in front of you if cart1_staticdata.ahead == cart_uuid then return end - minetest.log("action","cart #"..cart1_staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos)) + --minetest.log("action","cart #"..cart1_staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos)) -- Standard Collision Handling local cart2_staticdata = get_cart_data(cart_uuid) @@ -549,7 +549,7 @@ local function do_detached_movement(self, dtime) -- Use vector projection to only keep the velocity in the new direction of movement on the rail -- https://en.wikipedia.org/wiki/Vector_projection staticdata.velocity = vector.dot(staticdata.dir,freebody_velocity) - print("Reattached velocity="..tostring(staticdata.velocity)..", freebody_velocity="..tostring(freebody_velocity)) + --print("Reattached velocity="..tostring(staticdata.velocity)..", freebody_velocity="..tostring(freebody_velocity)) -- Clear freebody movement self.object:set_velocity(vector.zero()) From 9ef2e024638a8c36a99192ff92702e93696d6a33 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 31 Aug 2024 16:44:19 -0500 Subject: [PATCH 141/195] Add guard that prevents crash when itemstack is nil --- mods/CORE/vl_legacy/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/CORE/vl_legacy/init.lua b/mods/CORE/vl_legacy/init.lua index ca89f799c..cb6c6389c 100644 --- a/mods/CORE/vl_legacy/init.lua +++ b/mods/CORE/vl_legacy/init.lua @@ -18,7 +18,7 @@ function mod.convert_inventory_lists(lists) for _,list in pairs(lists) do for i = 1,#list do local itemstack = list[i] - local conversion = item_conversions[itemstack:get_name()] + local conversion = itemstack and item_conversions[itemstack:get_name()] if conversion then local new_name,func = conversion[1],conversion[2] if func then From 0119814c0dc74e4b2b96cc65a512cf36cb1e0cff Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 1 Sep 2024 16:47:57 -0500 Subject: [PATCH 142/195] Prevent trains from slowing on 45 degree track --- mods/ENTITIES/mcl_minecarts/train.lua | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index 279c21f0e..aab26fcc9 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -89,24 +89,14 @@ end mod.break_train_at = break_train_at function mod.update_train(staticdata) - --local staticdata = cart._staticdata - -- Only update from the back if staticdata.behind or not staticdata.ahead then return end - --print("\nUpdating train") -- Do no special processing if the cart is not part of a train if not staticdata.ahead and not staticdata.behind then return end -- Calculate the maximum velocity of all train cars - local velocity = 0 - local count = 0 - for cart in train_cars(staticdata) do - velocity = velocity + (cart.velocity or 0) - count = count + 1 - end - velocity = velocity / count - --print("Using velocity "..tostring(velocity)) + local velocity = staticdata.velocity -- Set the entire train to the average velocity local behind = nil @@ -132,7 +122,7 @@ function mod.update_train(staticdata) --[[ print(tostring(c.behind).."->"..c.uuid.."->"..tostring(c.ahead).."("..tostring(separation)..") setting cart #".. c.uuid.." velocity from "..tostring(c.velocity).." to "..tostring(cart_velocity)) - ]] + --]] c.velocity = cart_velocity behind = c From e7c550858d09c7869ec827d34397443f4603244b Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 12 Sep 2024 08:28:34 -0500 Subject: [PATCH 143/195] Calculate acceleration of trains based on average of acceleration for all carts in the train, make velocity not change on slopes and 45 degree track --- mods/ENTITIES/mcl_minecarts/movement.lua | 19 +++++++++++++++++-- mods/ENTITIES/mcl_minecarts/train.lua | 4 +++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index b60fbfe25..e0fb8f22f 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -305,8 +305,23 @@ local function do_movement_step(staticdata, dtime) -- Calculate timestep remaiing in this block local x_0 = staticdata.distance or 0 local remaining_in_block = 1 - x_0 - local a = calculate_acceleration(staticdata) + + -- Calculate acceleration local v_0 = staticdata.velocity + local a = 0 + if staticdata.ahead or staticdata.behind then + -- Calculate acceleration of the entire train + local count = 0 + for cart in mod.train_cars(staticdata) do + count = count + 1 + if cart.behind then + a = a + calculate_acceleration(cart) + end + end + a = a / count + else + a = calculate_acceleration(staticdata) + end -- Repel minecarts local away = direction_away_from_players(staticdata) @@ -370,7 +385,7 @@ local function do_movement_step(staticdata, dtime) end -- Calculate x_1 - local x_1 = x_0 + timestep * v_0 + 0.5 * a * timestep * timestep + local x_1 = x_0 + (timestep * v_0 + 0.5 * a * timestep * timestep) / vector.length(staticdata.dir) -- Update position and velocity of the minecart staticdata.velocity = v_1 diff --git a/mods/ENTITIES/mcl_minecarts/train.lua b/mods/ENTITIES/mcl_minecarts/train.lua index aab26fcc9..c21cbb0eb 100644 --- a/mods/ENTITIES/mcl_minecarts/train.lua +++ b/mods/ENTITIES/mcl_minecarts/train.lua @@ -20,7 +20,7 @@ local function find_back(start) end -- Iterate across all the cars in a train -local function train_cars(staticdata) +function mod.train_cars(staticdata) assert(staticdata) local back = find_back(staticdata) @@ -38,6 +38,8 @@ local function train_cars(staticdata) return ret end end +local train_cars = mod.train_cars + function mod.train_length(cart) local count = 0 for cart in train_cars(cart) do From b138add4980864676b47430cd48cac1929fd189e Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 17 Sep 2024 05:57:35 -0500 Subject: [PATCH 144/195] Fix another crash, fix rail tee on texture --- mods/ENTITIES/mcl_minecarts/movement.lua | 2 +- textures/default_rail_t_junction_on.png | Bin 4375 -> 241 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index e0fb8f22f..5a8f023ed 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -307,7 +307,7 @@ local function do_movement_step(staticdata, dtime) local remaining_in_block = 1 - x_0 -- Calculate acceleration - local v_0 = staticdata.velocity + local v_0 = staticdata.velocity or 0 local a = 0 if staticdata.ahead or staticdata.behind then -- Calculate acceleration of the entire train diff --git a/textures/default_rail_t_junction_on.png b/textures/default_rail_t_junction_on.png index b605180e159b1b29cf621588f822bbb878c136f6..cfaece8331d044fa4a54e9a1198c211b91953dab 100644 GIT binary patch delta 213 zcmbQP^pSCbO1)x$Plzi61B0ihr;d(JO}tNGn2W8gZIZXOva)h#mfyU}1XEK}re_~a zfQlGPg8YIR9G=}s19Iv-T^vI=qLUK@k{Fm=h1ocqLwg!6?<7Avqv^#h88J;=b@xgQ zEyIG2u#%E3nra3HELsKyD%wn@EPCk~n^_E}u^U?Z`US8Wx?I(fbm(#I>6Ve$A|WwD ze5Z%#%n(z94XmoB>$FyRB_wbb<(ct>1U20_x+7#lqoYiYwScoEL$!sZfA)`u!9b@l Nc)I$ztaD0e0syICL6rai literal 4375 zcmeHKdu$xV8Q-(x^VP9YUN~;ri0vMEIAm|1=eynb9AclHTg7*F?JESAD&E_jy)3@R z=5B4DLsA8dX%h0Fv{6Hp5TvFl#9)<1m8fY!l`Uv#QX!QRs1wIZ)i`QJLlh`NEzocG z&My_IvQ+-_S#$Hv%x`}4&2PT>&U2tOvbCaYRT+k16;deJ2K5rFEGmZY_pe;DI^IIt zB~6kxU@m9?(y<70SQsddS$2`57_(+Q4rLkG7RD;893^&r2<)#F>>q-?zJMtLn{gCj z6;Pjq!h*d5>T^)^@8#Ee6>RI{vj0M0S+yvmS=_a5XTWW_gk3L{`8VAAE}( z2WZE$Gagw?_l&*q4zTp=4GpbQLj!JP(wd$?7?wM^_xpj+Nb8D$miC_zca}b}f4%x# zr01JU-{=@1qOM=m58kns`m}#-g>>+VA5U}`AH-k!+r+gO2Kxtb*Ju0uoBY+op#vYU z8K`9HUjEh5+vCFJ-n(#qL-YCJYx~ZQY2wpM&j0dT%!dc@CMLAzw-?y!cUPBO{iypW zcJ*eg_V=HUpB`+v`0aAn{?T39@tRxCTNlnIjq!Cye)iN0%T6zEIsP_&%YEt2qgVYa zzf+mhMlYP1Jm4OUz0`l~4sA`l`~>}R#Wv-|x=$95^zB7Y|2^ss z#g=Y+60hGra@Ri9cfm7LkeH`if z?&i#6nX{v(M#mupb9!{Uxjo!0D(R$0R?{)$$t4Zzys+BMIYU;ukcr1oTu%jvo3Fe^ z;JO+h?)Qf2u+e~a>Y?2k)V@0sRd#nNf=X~+v=tW z+=iH40b)CZQA0X|aNfgvXtFV<_pn4=8D5)FHL)$&RG@%XfY@o8hDcGpy}h1Z&Xdl> zDMk_QZ`%8zR{gY z*xOMR3MEhypjlXznX{yI;ihL;62$eSVb21w=O9g8n-Ob{Z`O#to!O4S^l9EX=&8Ni zU_gb#Vlb_ATkc800AZ~!s%b@6Mcd?LzhC7QniOb`BY8$fq|7iHDf7Hadr=Hh1UrjL zN@Yzsr67w6$UQpn2%Ji5s757Yg20lzO#8{0PvuD;rzptB37ikjrfA9N5S4ObwpSLF z3RE)UX;j11kbW9zB=2SYr0ny0Nj6ravaFY8ecn8ks)$YLOj3s9)RS@?QAR3myJ-n7 zZfTVQ1nZ$^dRh~*slkK*(X6Msb29@`J&D>)*;147F^td4a6UgQ;}f_U(0wSAg-EnG z?bKUCvfG5l$~-ajv#v!!gZ=&|H4pURaNlSkb=s`NWO-aNm&yF68Tk4hzT6WAbu`; zHm#Yxat3XQgN~pTgs1%jg|D{{s(LQEcPFw=0R$syj%0-?!TbfmsKSORD`!mYxR&}a zO=@kxtRw^T@_mrJAQw{8$*`cAm3F?wPvI=S#1(*gVUc<1yCBzsT=P<3Uf_l3T99jA z3d{?<5MBQ_xyojqQz!-h1@*#XspG+m@Fd3WZf%Y>+D|S_k|ej=y>a74uh$z01a{pQ z+_R(kz(bMPwoT9Ubw2y>Yhl1S%(h57cJK9fHLDTZwl#?DFIjE9&A(!V9>`+Y;^kIx zU?)yiL1(Ecg&Rw66tAvaz4rQ_Kllu~_DaDmQP(Ak0_&AOokOo5 zDNjUx@YDE-udd>Z{STR@xhm4x>Eb&lj;CDFl{ZG;dY~lcFFC)CqDz<{IaqaXdAwHNdQ?+$z3+_1dr-ADg8wz&B4;cxUX!(}Y(dL$X&$6<$GuX^&I S;rd(P2qrZ~f Date: Tue, 17 Sep 2024 06:59:24 -0500 Subject: [PATCH 145/195] Add back shift+punch to immediately drop minecart --- mods/ENTITIES/mcl_minecarts/carts.lua | 139 +++++++++++++------------- 1 file changed, 71 insertions(+), 68 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index d730078f7..a4c46dba3 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -67,6 +67,71 @@ local function detach_driver(self) --print("No player object found for "..driver_name) end end +function mod.kill_cart(staticdata, killer) + local pos + mcl_log("cart #"..staticdata.uuid.." was killed") + + -- Leave nodes + if staticdata.attached_at then + handle_cart_leave(self, staticdata.attached_at, staticdata.dir ) + else + --mcl_log("TODO: handle detatched minecart death") + end + + -- Handle entity-related items + local le = mcl_util.get_luaentity_from_uuid(staticdata.uuid) + if le then + pos = le.object:get_pos() + + detach_driver(le) + + -- Detach passenger + if le._passenger then + local mob = le._passenger.object + mob:set_detach() + end + + -- Remove the entity + le.object:remove() + else + pos = mod.get_cart_position(staticdata) + end + + -- Drop items + if not staticdata.dropped then + -- Try to drop the cart + local entity_def = minetest.registered_entities[staticdata.cart_type] + if entity_def then + local drop_cart = true + if killer and minetest.is_creative_enabled(killer:get_player_name()) then + drop_cart = false + end + + if drop_cart then + local drop = entity_def.drop + for d=1, #drop do + minetest.add_item(pos, drop[d]) + end + end + end + + -- Drop any items in the inventory + local inventory = staticdata.inventory + if inventory then + for i=1,#inventory do + minetest.add_item(pos, inventory[i]) + end + end + + -- Prevent item duplication + staticdata.dropped = true + end + + -- Remove data + destroy_cart_data(staticdata.uuid) +end +local kill_cart = mod.kill_cart + -- Table for item-to-entity mapping. Keys: itemstring, Values: Corresponding entity ID local entity_mapping = {} @@ -192,10 +257,14 @@ function DEFAULT_CART_DEF:on_punch(puncher, time_from_last_punch, tool_capabilit if puncher == self._driver then return end local staticdata = self._staticdata + + if puncher:get_player_control().sneak then + mod.kill_cart(staticdata, puncher) + return + end + local controls = staticdata.controls or {} - local impulse = vector.multiply(dir, damage * 20) - local accel = vector.dot(staticdata.dir, impulse) if accel < 0 and staticdata.velocity == 0 then mod.reverse_direction(staticdata) @@ -278,72 +347,6 @@ function DEFAULT_CART_DEF:on_step(dtime) mod.update_cart_orientation(self) end -function mod.kill_cart(staticdata, killer) - local pos - mcl_log("cart #"..staticdata.uuid.." was killed") - - -- Leave nodes - if staticdata.attached_at then - handle_cart_leave(self, staticdata.attached_at, staticdata.dir ) - else - --mcl_log("TODO: handle detatched minecart death") - end - - -- Handle entity-related items - local le = mcl_util.get_luaentity_from_uuid(staticdata.uuid) - if le then - pos = le.object:get_pos() - - detach_driver(le) - - -- Detach passenger - if le._passenger then - local mob = le._passenger.object - mob:set_detach() - end - - -- Remove the entity - le.object:remove() - else - pos = mod.get_cart_position(staticdata) - end - - -- Drop items - if not staticdata.dropped then - - -- Try to drop the cart - local entity_def = minetest.registered_entities[staticdata.cart_type] - if entity_def then - local drop_cart = true - if killer and minetest.is_creative_enabled(killer:get_player_name()) then - drop_cart = false - end - - if drop_cart then - local drop = entity_def.drop - for d=1, #drop do - minetest.add_item(pos, drop[d]) - end - end - end - - -- Drop any items in the inventory - local inventory = staticdata.inventory - if inventory then - for i=1,#inventory do - minetest.add_item(pos, inventory[i]) - end - end - - -- Prevent item duplication - staticdata.dropped = true - end - - -- Remove data - destroy_cart_data(staticdata.uuid) -end -local kill_cart = mod.kill_cart - function DEFAULT_CART_DEF:on_death(killer) kill_cart(self._staticdata, killer) end From acd8195d9bc14e2b845acd3c95bcc100076e2ebf Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 13 Oct 2024 14:22:11 -0500 Subject: [PATCH 146/195] Fix right-clicking nodes while holding rail --- mods/ENTITIES/mcl_minecarts/rails.lua | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 00c78616d..4199e141f 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -127,13 +127,25 @@ local BASE_DEF = { _doc_items_usagehelp = railuse, _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), on_place = function(itemstack, placer, pointed_thing) - local node_name = minetest.get_node(pointed_thing.under).name + local node = minetest.get_node(pointed_thing.under) + local node_name = node.name + -- Don't allow placing rail above rail - if minetest.get_item_group(node_name,"rail") == 0 then - return minetest.item_place_node(itemstack, placer, pointed_thing) - else + if minetest.get_item_group(node_name,"rail") ~= 0 then return itemstack end + + -- Handle right-clicking nodes with right-click handlers + if placer and not placer:get_player_control().sneak then + local node_def = minetest.registered_nodes[node_name] or {} + local on_rightclick = node_def and node_def.on_rightclick + if on_rightclick then + return on_rightclick(pointed_thing.under, node, placer, itemstack, pointed_thing) or itemstack + end + end + + -- Place the rail + return minetest.item_place_node(itemstack, placer, pointed_thing) end, after_place_node = function(pos, placer, itemstack, pointed_thing) update_rail_connections(pos) From 0ddecdff2a6270a2dd0a1ecddf56e67582c68b30 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 13 Oct 2024 14:37:40 -0500 Subject: [PATCH 147/195] Add workaround to prevent random detached inventory doesn't exist warning when right-clicking entities with inventory --- mods/ENTITIES/mcl_entity_invs/init.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_entity_invs/init.lua b/mods/ENTITIES/mcl_entity_invs/init.lua index ee9b50318..4bcffede4 100644 --- a/mods/ENTITIES/mcl_entity_invs/init.lua +++ b/mods/ENTITIES/mcl_entity_invs/init.lua @@ -113,7 +113,11 @@ function mcl_entity_invs.show_inv_form(ent,player,text) local playername = player:get_player_name() - minetest.show_formspec(playername, ent._inv_id, load_default_formspec (ent, text)) + -- Workaround: wait at least 50ms to ensure that the detached inventory exists before + -- the formspec attempts to use it. (See https://git.minetest.land/VoxeLibre/VoxeLibre/issues/4670#issuecomment-84875) + minetest.after(0.05, function() + minetest.show_formspec(playername, ent._inv_id, load_default_formspec (ent, text)) + end) end local function drop_inv(ent) From cc74a5e75e227f0f19654becc434ce3b38e86155 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 28 Oct 2024 21:26:44 -0500 Subject: [PATCH 148/195] Fix cart pitch when detached --- mods/ENTITIES/mcl_minecarts/carts.lua | 5 +++-- mods/ENTITIES/mcl_minecarts/movement.lua | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index a4c46dba3..c4749153e 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -341,11 +341,12 @@ function DEFAULT_CART_DEF:on_step(dtime) end end + if not staticdata.connected_at then do_detached_movement(self, dtime) + else + mod.update_cart_orientation(self) end - - mod.update_cart_orientation(self) end function DEFAULT_CART_DEF:on_death(killer) kill_cart(self._staticdata, killer) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 5a8f023ed..49feb8e9e 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -572,6 +572,12 @@ local function do_detached_movement(self, dtime) return end end + + -- Reset pitch if still not attached + local rot = self.object:get_rotation() + minetest.log(vector.to_string(rot)) + rot.x = 0 + self.object:set_rotation(rot) end --return do_movement, do_detatched_movement From fd559ec40dd572de38124083ce08619c977bef19 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 28 Oct 2024 21:30:44 -0500 Subject: [PATCH 149/195] Fix crash with minecart on activator rail --- mods/ENTITIES/mcl_minecarts/carts.lua | 2 ++ mods/ENTITIES/mcl_minecarts/carts/minecart.lua | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index c4749153e..9b9cc0d6a 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -67,6 +67,8 @@ local function detach_driver(self) --print("No player object found for "..driver_name) end end +mod.detach_driver = detach_driver + function mod.kill_cart(staticdata, killer) local pos mcl_log("cart #"..staticdata.uuid.." was killed") diff --git a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua index b736a4c03..600eedc5a 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua @@ -7,7 +7,7 @@ local mod = mcl_minecarts local PASSENGER_ATTACH_POSITION = mod.PASSENGER_ATTACH_POSITION local function activate_normal_minecart(self) - detach_driver(self) + mod.detach_driver(self) -- Detach passenger if self._passenger then From 3e6fe24a57697316e7b194da567d5bfd9cde2d15 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 29 Oct 2024 07:45:00 -0500 Subject: [PATCH 150/195] Remove instance of debug logging, change movement.lua function export --- mods/ENTITIES/mcl_minecarts/carts.lua | 14 +++++++------- mods/ENTITIES/mcl_minecarts/movement.lua | 9 +++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 9b9cc0d6a..3f5085b6d 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -13,10 +13,10 @@ local save_cart_data = mod.save_cart_data local update_cart_data = mod.update_cart_data local destroy_cart_data = mod.destroy_cart_data local find_carts_by_block_map = mod.find_carts_by_block_map -local do_movement,do_detached_movement,handle_cart_enter = dofile(modpath.."/movement.lua") -assert(do_movement) -assert(do_detached_movement) -assert(handle_cart_enter) +local movement = dofile(modpath.."/movement.lua") +assert(movement.do_movement) +assert(movement.do_detached_movement) +assert(movement.handle_cart_enter) -- Constants local max_step_distance = 0.5 @@ -345,7 +345,7 @@ function DEFAULT_CART_DEF:on_step(dtime) if not staticdata.connected_at then - do_detached_movement(self, dtime) + movement.do_detached_movement(self, dtime) else mod.update_cart_orientation(self) end @@ -407,7 +407,7 @@ function mod.place_minecart(itemstack, pointed_thing, placer) end if railpos then - handle_cart_enter(staticdata, railpos) + movement.handle_cart_enter(staticdata, railpos) end local pname = placer and placer:get_player_name() or "" @@ -619,7 +619,7 @@ minetest.register_globalstep(function(dtime) --- Non-entity code if staticdata.connected_at then - do_movement(staticdata, dtime) + movement.do_movement(staticdata, dtime) end end diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 49feb8e9e..8e8132996 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -2,6 +2,7 @@ local modname = minetest.get_current_modname() local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts local S = minetest.get_translator(modname) +local submod = {} -- Constants local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", "Minecart Debug") @@ -92,6 +93,7 @@ local function handle_cart_enter(staticdata, pos, next_dir) set_metadata_cart_status(pos, staticdata.uuid, 1) handle_cart_enter_exit(staticdata, pos, next_dir, "on_enter" ) end +submod.handle_cart_enter = handle_cart_enter local function handle_cart_leave(staticdata, pos, next_dir) --print("leaving "..tostring(pos)) set_metadata_cart_status(pos, staticdata.uuid, nil) @@ -476,7 +478,7 @@ local function do_movement_step(staticdata, dtime) return dtime - timestep end -local function do_movement( staticdata, dtime ) +function submod.do_movement( staticdata, dtime ) assert(staticdata) -- Allow the carts to be delay for the rest of the world to react before moving again @@ -504,7 +506,7 @@ local function do_movement( staticdata, dtime ) end end -local function do_detached_movement(self, dtime) +function submod.do_detached_movement(self, dtime) local staticdata = self._staticdata -- Make sure the object is still valid before trying to move it @@ -575,11 +577,10 @@ local function do_detached_movement(self, dtime) -- Reset pitch if still not attached local rot = self.object:get_rotation() - minetest.log(vector.to_string(rot)) rot.x = 0 self.object:set_rotation(rot) end --return do_movement, do_detatched_movement -return do_movement,do_detached_movement,handle_cart_enter +return submod From 295589f032635d4a21c43206a7adfd5e1bf8cd7d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 29 Oct 2024 07:58:52 -0500 Subject: [PATCH 151/195] Allow functions for _rail_acceleration, make powered rail only accelerate carts when one side of the rail is a solid block or stairs --- mods/ENTITIES/mcl_minecarts/movement.lua | 7 ++++++- mods/ENTITIES/mcl_minecarts/rails/powered.lua | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 8e8132996..b6ee1f12f 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -287,7 +287,12 @@ local function calculate_acceleration(staticdata) elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or SPEED_MAX ) then -- Standard friction elseif node_def and node_def._rail_acceleration then - acceleration = node_def._rail_acceleration * 4 + local rail_accel = node_def._rail_acceleration + if type(rail_accel) == "function" then + acceleration = (rail_accel(pos, staticdata) or 0) * 4 + else + acceleration = rail_accel * 4 + end end -- Factor in gravity after everything else diff --git a/mods/ENTITIES/mcl_minecarts/rails/powered.lua b/mods/ENTITIES/mcl_minecarts/rails/powered.lua index dcad6a197..94bb4f1d9 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/powered.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/powered.lua @@ -43,7 +43,21 @@ mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_ -- Powered rail (on = acceleration mode) mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_rail_golden_powered.png" },{ _doc_items_create_entry = false, - _rail_acceleration = 4, + _rail_acceleration = function(pos, staticdata) + local dir = mod.get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) + local node_a = minetest.get_node(vector.add(pos, dir)) + local node_b = minetest.get_node(vector.add(pos, -dir)) + local has_adjacent_solid = minetest.get_item_group(node_a.name, "solid") ~= 0 or + minetest.get_item_group(node_b.name, "solid") ~= 0 or + minetest.get_item_group(node_a.name, "stair") ~= 0 or + minetest.get_item_group(node_b.name, "stair") ~= 0 + + if has_adjacent_solid then + return 4 + else + return 0 + end + end, _max_acceleration_velocity = 8, groups = { not_in_creative_inventory = 1, From e025070233d7ea179559e894c6bf7f7e9ca09a60 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 30 Oct 2024 07:47:42 -0500 Subject: [PATCH 152/195] Fix friction on slopes, fix cart reversal when timestep is very small or zero --- mods/ENTITIES/mcl_minecarts/movement.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index b6ee1f12f..fc9b4279b 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -7,7 +7,7 @@ local submod = {} -- Constants local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", "Minecart Debug") --DEBUG = false ---mcl_debug = function(msg) print(msg) end +--mcl_debug,DEBUG = function(msg) print(msg) end,true -- Imports local env_physics @@ -362,6 +362,9 @@ local function do_movement_step(staticdata, dtime) -- Would stop or reverse direction inside this block, calculate time to v_1 = 0 timestep = -v_0 / a stops_in_block = true + if timestep <= 0.01 then + reverse_direction(staticdata) + end elseif a ~= 0 then -- Setting x_1 = x_0 + remaining_in_block, and solving for t gives: timestep = ( math.sqrt( v_0 * v_0 + 2 * a * remaining_in_block) - v_0 ) / a @@ -387,7 +390,7 @@ local function do_movement_step(staticdata, dtime) local v_1 = v_0 + a * timestep if v_1 > v_max then v_1 = v_max - elseif v_1 < FRICTION / 5 then + elseif v_1 < 0.025 then v_1 = 0 end @@ -398,7 +401,7 @@ local function do_movement_step(staticdata, dtime) staticdata.velocity = v_1 staticdata.distance = x_1 - if DEBUG and (1==0) and ( v_0 > 0 or a ~= 0 ) then + if DEBUG and ( v_0 > 0 or a ~= 0 ) then mcl_debug( "- cart #"..tostring(staticdata.uuid).. ": a="..tostring(a).. ",v_0="..tostring(v_0).. From 1503aaeced8ea66ff8a36b6c1191b8dcf7ae46ec Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 3 Nov 2024 07:38:54 -0600 Subject: [PATCH 153/195] Fix vertical movement that was hitting 45 degree curve code, reset pitch on minecarts --- mods/ENTITIES/mcl_minecarts/carts.lua | 1 + mods/ENTITIES/mcl_minecarts/functions.lua | 15 ++++++++------- mods/ENTITIES/mcl_minecarts/movement.lua | 5 +++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 3f5085b6d..39770ebc5 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -296,6 +296,7 @@ function DEFAULT_CART_DEF:on_step(dtime) if staticdata.seq and self._seq < staticdata.seq then --print("removing cart #"..staticdata.uuid.." with sequence number mismatch") self.object:remove() + self._removed = true return end diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index caac7daf8..20ece5f9c 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -2,7 +2,7 @@ local vector = vector local mod = mcl_minecarts local table_merge = mcl_util.table_merge -function get_path(base, first, ...) +local function get_path(base, first, ...) if not first then return base end if not base then return end return get_path(base[first], ...) @@ -311,11 +311,10 @@ end local function get_rail_direction_inner(pos, dir) -- Handle new track types that have track-specific direction handler local node = minetest.get_node(pos) - local node_def = minetest.registered_nodes[node.name] - local get_next_dir = get_path(node_def,"_mcl_minecarts","get_next_dir") + local get_next_dir = get_path(minetest.registered_nodes,node.name,"_mcl_minecarts","get_next_dir") if not get_next_dir then return dir end - dir = node_def._mcl_minecarts.get_next_dir(pos, dir, node) + dir = get_next_dir(pos, dir, node) -- Handle reversing if there is a solid block in the next position local next_pos = vector.add(pos, dir) @@ -367,13 +366,15 @@ function mcl_minecarts.get_rail_direction(self, pos_, dir) local new_dir = get_rail_direction_inner(pos, dir) + if new_dir.y ~= 0 then return new_dir end + -- Check four 45 degree movement local next_rails_dir = get_rail_direction_inner(vector.add(pos, new_dir), new_dir) - if vector.equals(next_rails_dir, dir) and not vector.equals(new_dir, next_rails_dir) then + if dir.y == 0 and vector.equals(next_rails_dir, dir) and not vector.equals(new_dir, next_rails_dir) then return vector.add(new_dir, next_rails_dir) - else - return new_dir end + + return new_dir end function mod.update_cart_orientation(self) local staticdata = self._staticdata diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index fc9b4279b..bebdb778f 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -539,6 +539,11 @@ function submod.do_detached_movement(self, dtime) self.object:set_acceleration(accel) end + -- Reset pitch + local rot = self.object:get_rotation() + rot.y = 0 + self.object:set_rotation(rot) + local away = vector_away_from_players(self, staticdata) if away then local v = self.object:get_velocity() From 8e2a58a4c63945d28d6c70b0bcdc10e686d18f41 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 3 Nov 2024 07:44:28 -0600 Subject: [PATCH 154/195] Finish restricting 45 degree movement code to horizontal plane --- mods/ENTITIES/mcl_minecarts/functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 20ece5f9c..8d45a4cd9 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -370,7 +370,7 @@ function mcl_minecarts.get_rail_direction(self, pos_, dir) -- Check four 45 degree movement local next_rails_dir = get_rail_direction_inner(vector.add(pos, new_dir), new_dir) - if dir.y == 0 and vector.equals(next_rails_dir, dir) and not vector.equals(new_dir, next_rails_dir) then + if next_rails_dir.y == 0 and vector.equals(next_rails_dir, dir) and not vector.equals(new_dir, next_rails_dir) then return vector.add(new_dir, next_rails_dir) end From c8611b08f94545d5b11422c8801e1a015e169857 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 8 Nov 2024 07:03:18 -0600 Subject: [PATCH 155/195] Make straight rails with a free end bend towards newly placed rail and form corners --- mods/ENTITIES/mcl_minecarts/functions.lua | 109 +++++++++++++++++----- 1 file changed, 85 insertions(+), 24 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 8d45a4cd9..f0012cea5 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -225,30 +225,7 @@ local function get_rail_connections(pos, opt) end mod.get_rail_connections = get_rail_connections -local function update_rail_connections(pos, opt) - local ignore_neighbor_connections = opt and opt.ignore_neighbor_connections - - local node = minetest.get_node(pos) - local nodedef = minetest.registered_nodes[node.name] - if not nodedef or not nodedef._mcl_minecarts then return end - - -- Get the mappings to use - local rules = HORIZONTAL_RULES_BY_RAIL_GROUP[nodedef.groups.rail] - if nodedef._mcl_minecarts and nodedef._mcl_minecarts.connection_rules then -- Custom connection rules - rules = nodedef._mcl_minecarts.connection_rules - end - if not rules then return end - - -- Horizontal rules, Check for rails on each neighbor - local connections = get_rail_connections(pos, opt) - - -- Check for rasing rails to slopes - for i = 1,#CONNECTIONS do - local dir = CONNECTIONS[i] - local neighbor = vector.add(pos, dir) - make_sloped_if_straight( vector.offset(neighbor, 0, -1, 0), dir ) - end - +local function apply_connection_rules(node, nodedef, pos, rules, connections) -- Select the best allowed connection local rule = nil local score = 0 @@ -276,6 +253,90 @@ local function update_rail_connections(pos, opt) rule.after(rule, pos, connections) end end +end + +local function is_rail_end_connected(pos, dir) + -- Handle new track types that have track-specific direction handler + local node = force_get_node(pos) + local get_next_dir = get_path(minetest.registered_nodes,node.name,"_mcl_minecarts","get_next_dir") + if not get_next_dir then return false end + + return get_next_dir(pos, dir, node) == dir +end + +local function bend_straight_rail(pos, towards) + dir = CONNECTIONS[i] + local node = force_get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + + -- Only bend rails + local rail_type = minetest.get_item_group(node.name, "rail") + if rail_type == 0 then return end + + -- Only bend unbent rails + if node.name ~= nodedef._mcl_minecarts.base_name then return end + + -- only bend rails that have at least one free end + local dir1 = minetest.fourdir_to_dir(node.param2) + local dir2 = minetest.fourdir_to_dir((node.param2+2)%4) + local dir1_connected = is_rail_end_connected(pos + dir1, dir2) + local dir2_connected = is_rail_end_connected(pos + dir2, dir1) + if dir1_connected and dir2_connected then return end + + -- TODO: bend the rail + local connections = { + vector.direction(pos, towards), + } + if dir1_connected then + connections[#connections+1] = dir1 + end + if dir2_connected then + connections[#connections+1] = dir2 + end + local connections_mask = 0 + for i = 1,#CONNECTIONS do + for j = 1,#connections do + if CONNECTIONS[i] == connections[j] then + connections_mask = bit.bor(connections_mask, bit.lshift(1, i -1)) + end + end + end + + local rules = HORIZONTAL_RULES_BY_RAIL_GROUP[nodedef.groups.rail] + apply_connection_rules(node, nodedef, pos, rules, connections_mask) +end + +local function update_rail_connections(pos, opt) + local ignore_neighbor_connections = opt and opt.ignore_neighbor_connections + + local node = minetest.get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + if not nodedef or not nodedef._mcl_minecarts then return end + + -- Get the mappings to use + local rules = HORIZONTAL_RULES_BY_RAIL_GROUP[nodedef.groups.rail] + if nodedef._mcl_minecarts and nodedef._mcl_minecarts.connection_rules then -- Custom connection rules + rules = nodedef._mcl_minecarts.connection_rules + end + if not rules then return end + + if not (opt and opt.no_bend_straights) then + for i = 1,#CONNECTIONS do + bend_straight_rail(vector.add(pos, CONNECTIONS[i]), pos) + end + end + + -- Horizontal rules, Check for rails on each neighbor + local connections = get_rail_connections(pos, opt) + + -- Check for rasing rails to slopes + for i = 1,#CONNECTIONS do + local dir = CONNECTIONS[i] + local neighbor = vector.add(pos, dir) + make_sloped_if_straight( vector.offset(neighbor, 0, -1, 0), dir ) + end + + apply_connection_rules(node, nodedef, pos, rules, connections) local node_def = minetest.registered_nodes[node.name] if get_path(node_def, "_mcl_minecarts", "can_slope") then From 6143832ee39571fb725d616fe58a2322e3da3a1f Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 8 Nov 2024 07:19:33 -0600 Subject: [PATCH 156/195] Fix crash --- mods/ENTITIES/mcl_minecarts/functions.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index f0012cea5..85895f54c 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -274,6 +274,7 @@ local function bend_straight_rail(pos, towards) if rail_type == 0 then return end -- Only bend unbent rails + if not nodedef._mcl_minecarts then return end if node.name ~= nodedef._mcl_minecarts.base_name then return end -- only bend rails that have at least one free end From 56a5a28e586fe64f5ecf07968dd3cae9881e40a6 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Nov 2024 09:09:58 -0600 Subject: [PATCH 157/195] Fix minecart entity rotation --- mods/ENTITIES/mcl_minecarts/functions.lua | 24 +++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 85895f54c..428b56b11 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -438,38 +438,45 @@ function mcl_minecarts.get_rail_direction(self, pos_, dir) return new_dir end + +local _2_pi = math.pi * 2 +local _half_pi = math.pi * 0.5 +local _quart_pi = math.pi * 0.5 +local pi = math.pi +local rot_debug = {} function mod.update_cart_orientation(self) local staticdata = self._staticdata - - -- constants - local _2_pi = math.pi * 2 - local pi = math.pi local dir = staticdata.dir -- Calculate an angle from the x,z direction components - local rot_y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) + local rot_y = math.atan2( dir.z, dir.x ) + ( staticdata.rot_adjust or 0 ) if rot_y < 0 then rot_y = rot_y + _2_pi end -- Check if the rotation is a 180 flip and don't change if so local rot = self.object:get_rotation() + local old_rot = vector.new(rot) + rot.y = (rot.y - _half_pi + _2_pi) % _2_pi if not rot then return end local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) + local flipped = false if diff < 0.001 or diff > _2_pi - 0.001 then -- Update rotation adjust and recalculate the rotation staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi - rot.y = math.atan2( dir.x, dir.z ) + ( staticdata.rot_adjust or 0 ) + local new_rot_y = math.atan2( dir.z, dir.x ) + ( staticdata.rot_adjust or 0 ) + assert((math.abs(rot.y -new_rot_y) % _2_pi) < 0.001, "math is wrong: "..tostring(new_rot_y).." ~= "..tostring(rot.y)) + flipped = true else rot.y = rot_y end -- Forward/backwards tilt (pitch) if dir.y < 0 then - rot.x = -0.25 * pi + rot.x = -_quart_pi elseif dir.y > 0 then - rot.x = 0.25 * pi + rot.x = _quart_pi else rot.x = 0 end @@ -481,6 +488,7 @@ function mod.update_cart_orientation(self) rot.x = -rot.x end + rot.y = (rot.y + _half_pi) % _2_pi self.object:set_rotation(rot) end From cea82c07f8b17ac5cdde32e55a238df5d3f182c6 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Nov 2024 09:21:23 -0600 Subject: [PATCH 158/195] Remove debug code --- mods/ENTITIES/mcl_minecarts/functions.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 428b56b11..97933f2ef 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -461,13 +461,9 @@ function mod.update_cart_orientation(self) if not rot then return end local diff = math.abs((rot_y - ( rot.y + pi ) % _2_pi) ) - local flipped = false if diff < 0.001 or diff > _2_pi - 0.001 then - -- Update rotation adjust and recalculate the rotation + -- Update rotation adjust staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi - local new_rot_y = math.atan2( dir.z, dir.x ) + ( staticdata.rot_adjust or 0 ) - assert((math.abs(rot.y -new_rot_y) % _2_pi) < 0.001, "math is wrong: "..tostring(new_rot_y).." ~= "..tostring(rot.y)) - flipped = true else rot.y = rot_y end From 524fe37f4a316838c9e0ac029b0d1b5338e38633 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Nov 2024 09:41:08 -0600 Subject: [PATCH 159/195] Use correct vectors for look directions east/west --- mods/ENTITIES/mcl_minecarts/movement.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index bebdb778f..2cbfe4ec9 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -250,6 +250,12 @@ local function direction_away_from_players(staticdata) return 0 end +local look_directions = { + mod.north, + mod.west, + mod.south, + mod.east, +} local function calculate_acceleration(staticdata) local acceleration = 0 @@ -270,7 +276,7 @@ local function calculate_acceleration(staticdata) if (ctrl.forward or 0) > time_active then if staticdata.velocity == 0 then - local look_dir = minetest.facedir_to_dir(ctrl.look or 0) + local look_dir = look_directions[ctrl.look or 0] local dot = vector.dot(staticdata.dir, look_dir) if dot < 0 then reverse_direction(staticdata) From fcbb4f6e34cccbd560243571142618e845c308a0 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Nov 2024 12:33:39 -0600 Subject: [PATCH 160/195] Fix detector rail strangeness, prevent crash --- mods/ENTITIES/mcl_minecarts/movement.lua | 2 +- mods/ENTITIES/mcl_minecarts/rails/detector.lua | 16 ++++------------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 2cbfe4ec9..417660e00 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -276,7 +276,7 @@ local function calculate_acceleration(staticdata) if (ctrl.forward or 0) > time_active then if staticdata.velocity == 0 then - local look_dir = look_directions[ctrl.look or 0] + local look_dir = look_directions[ctrl.look or 0] or mod.north local dot = vector.dot(staticdata.dir, look_dir) if dot < 0 then reverse_direction(staticdata) diff --git a/mods/ENTITIES/mcl_minecarts/rails/detector.lua b/mods/ENTITIES/mcl_minecarts/rails/detector.lua index b339c9335..80ae596cc 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/detector.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/detector.lua @@ -19,12 +19,8 @@ mod.register_straight_rail("mcl_minecarts:detector_rail_v2",{"mcl_minecarts_rail }, _mcl_minecarts_on_enter = function(pos, cart) local node = minetest.get_node(pos) - - local newnode = { - name = "mcl_minecarts:detector_rail_v2_on", - param2 = node.param2 - } - minetest.swap_node( pos, newnode ) + node.name = "mcl_minecarts:detector_rail_v2_on" + minetest.set_node( pos, node ) mesecon.receptor_on(pos) end, craft = { @@ -51,12 +47,8 @@ mod.register_straight_rail("mcl_minecarts:detector_rail_v2_on",{"mcl_minecarts_r }, _mcl_minecarts_on_leave = function(pos, cart) local node = minetest.get_node(pos) - - local newnode = { - name = "mcl_minecarts:detector_rail", - param2 = node.param2 - } - minetest.swap_node( pos, newnode ) + node.name = "mcl_minecarts:detector_rail_v2" + minetest.set_node( pos, node ) mesecon.receptor_off(pos) end, drop = "mcl_minecarts:detector_rail_v2", From f6f8c3e5bc7fdd8fdf5cb142953ed458538bfe23 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Nov 2024 12:36:57 -0600 Subject: [PATCH 161/195] Make powered rails always accelerate moving carts --- mods/ENTITIES/mcl_minecarts/rails/powered.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/rails/powered.lua b/mods/ENTITIES/mcl_minecarts/rails/powered.lua index 94bb4f1d9..f63b091f6 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/powered.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/powered.lua @@ -44,6 +44,10 @@ mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_ mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_rail_golden_powered.png" },{ _doc_items_create_entry = false, _rail_acceleration = function(pos, staticdata) + if staticdata.velocity ~= 0 then + return 4 + end + local dir = mod.get_rail_direction(pos, staticdata.dir, nil, nil, staticdata.railtype) local node_a = minetest.get_node(vector.add(pos, dir)) local node_b = minetest.get_node(vector.add(pos, -dir)) From fc38b6eea1dc8fdf1c6329b6622321378654640c Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Nov 2024 15:14:57 -0600 Subject: [PATCH 162/195] Add in working curved variants of special rails --- mods/ENTITIES/mcl_minecarts/rails.lua | 84 ++++++++++++++----- .../mcl_minecarts/rails/activator.lua | 38 ++++----- .../ENTITIES/mcl_minecarts/rails/detector.lua | 22 ++++- mods/ENTITIES/mcl_minecarts/rails/normal.lua | 2 +- mods/ENTITIES/mcl_minecarts/rails/powered.lua | 32 ++++--- 5 files changed, 112 insertions(+), 66 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 4199e141f..aa4cc2959 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -202,6 +202,57 @@ function mod.register_rail(itemstring, ndef) if craft then minetest.register_craft(craft) end end +local function make_mesecons(base_name, suffix, base_mesecons) + if not base_mesecons then + if suffix == "_tee_off" or suffix == "_tee_on" then + base_mesecons = {} + else + return + end + end + + local mesecons = table.copy(base_mesecons) + + if suffix == "_tee_off" then + mesecons.effector = base_mesecons.effector and table.copy(base_mesecons.effector) or {} + + local old_action_on = base_mesecons.effector and base_mesecons.effector.action_on + mesecons.effector.action_on = function(pos, node) + if old_action_on then old_action_on(pos, node) end + + node.name = base_name.."_tee_on" + minetest.set_node(pos, node) + end + mesecons.effector.rules = mesecons.effector.rules or mesecon.rules.alldirs + elseif suffix == "_tee_on" then + mesecons.effector = base_mesecons.effector and table.copy(base_mesecons.effector) or {} + + local old_action_off = base_mesecons.effector and base_mesecons.effector.action_off + mesecons.effector.action_off = function(pos, node) + if old_action_off then old_action_off(pos, node) end + + node.name = base_name.."_tee_off" + minetest.set_node(pos, node) + end + mesecons.effector.rules = mesecons.effector.rules or mesecon.rules.alldirs + end + + if mesecons.conductor then + mesecons.conductor = table.copy(base_mesecons.conductor) + + if mesecons.conductor.onstate then + mesecons.conductor.onstate = base_mesecons.conductor.onstate..suffix + end + if base_mesecons.conductor.offstate then + mesecons.conductor.offstate = base_mesecons.conductor.offstate..suffix + end + end + + minetest.log("mesecons for "..base_name..suffix.." is "..dump(mesecons)) + return mesecons +end + + function mod.register_straight_rail(base_name, tiles, def) def = def or {} local base_def = table.copy(BASE_DEF) @@ -226,6 +277,7 @@ function mod.register_straight_rail(base_name, tiles, def) table_merge(base_def,{ _mcl_minecarts = { railtype = "straight", + suffix = "", }, }) @@ -233,8 +285,9 @@ function mod.register_straight_rail(base_name, tiles, def) mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(sloped_def),{ _mcl_minecarts = { get_next_dir = rail_dir_sloped, + suffix = "_sloped", }, - mesecons = def.mesecons_sloped, + mesecons = make_mesecons(base_name, "_sloped", def.mesecons), tiles = { tiles[1] }, _mcl_minecarts = { railtype = "sloped", @@ -263,6 +316,7 @@ function mod.register_curves_rail(base_name, tiles, def) get_next_dir = rail_dir_straight, railtype = "straight", can_slope = true, + suffix = "", }, })) @@ -280,24 +334,19 @@ function mod.register_curves_rail(base_name, tiles, def) _mcl_minecarts = { get_next_dir = rail_dir_curve, railtype = "corner", + suffix = "_corner", }, + mesecons = make_mesecons(base_name, "_corner", def.mesecons), })) -- Tee variants mod.register_rail(base_name.."_tee_off", table_merge(table.copy(base_def),{ tiles = { tiles[3] }, - mesecons = { - effector = { - action_on = function(pos, node) - local new_node = {name = base_name.."_tee_on", param2 = node.param2} - minetest.swap_node(pos, new_node) - end, - rules = mesecon.rules.alldirs, - } - }, + mesecons = make_mesecons(base_name, "_tee_off", def.mesecons), _mcl_minecarts = { get_next_dir = rail_dir_tee_off, railtype = "tee", + suffix = "_tee_off", }, })) mod.register_rail(base_name.."_tee_on", table_merge(table.copy(base_def),{ @@ -305,16 +354,9 @@ function mod.register_curves_rail(base_name, tiles, def) _mcl_minecarts = { get_next_dir = rail_dir_tee_on, railtype = "tee", + suffix = "_tee_on", }, - mesecons = { - effector = { - action_off = function(pos, node) - local new_node = {name = base_name.."_tee_off", param2 = node.param2} - minetest.swap_node(pos, new_node) - end, - rules = mesecon.rules.alldirs, - } - } + mesecons = make_mesecons(base_name, "_tee_on", def.mesecons), })) -- Sloped variant @@ -323,7 +365,9 @@ function mod.register_curves_rail(base_name, tiles, def) _mcl_minecarts = { get_next_dir = rail_dir_sloped, railtype = "tee", + suffix = "_sloped", }, + mesecons = make_mesecons(base_name, "_sloped", def.mesecons), tiles = { tiles[1] }, })) @@ -333,7 +377,9 @@ function mod.register_curves_rail(base_name, tiles, def) _mcl_minecarts = { get_next_dir = rail_dir_cross, railtype = "cross", + suffix = "_cross", }, + mesecons = make_mesecons(base_name, "_cross", def.mesecons), })) end diff --git a/mods/ENTITIES/mcl_minecarts/rails/activator.lua b/mods/ENTITIES/mcl_minecarts/rails/activator.lua index 8306dfa71..593613b35 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/activator.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/activator.lua @@ -4,7 +4,13 @@ local mod = mcl_minecarts local S = minetest.get_translator(modname) -- Activator rail (off) -mod.register_straight_rail("mcl_minecarts:activator_rail_v2", {"mcl_minecarts_rail_activator.png"},{ +mod.register_curves_rail("mcl_minecarts:activator_rail_v2", { + "mcl_minecarts_rail_activator.png", + "mcl_minecarts_rail_activator_curved.png", + "mcl_minecarts_rail_activator_t_junction.png", + "mcl_minecarts_rail_activator_t_junction.png", + "mcl_minecarts_rail_activator_crossing.png" +},{ description = S("Activator Rail"), _tt_help = S("Track for minecarts").."\n"..S("Activates minecarts when powered"), _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts."), @@ -17,14 +23,6 @@ mod.register_straight_rail("mcl_minecarts:activator_rail_v2", {"mcl_minecarts_ra rules = mod.rail_rules_long, }, }, - mesecons_sloped = { - conductor = { - state = mesecon.state.off, - offstate = "mcl_minecarts:activator_rail_v2_sloped", - onstate = "mcl_minecarts:activator_rail_v2_on_sloped", - rules = mod.rail_rules_long, - }, - }, craft = { output = "mcl_minecarts:activator_rail_v2 6", recipe = { @@ -46,7 +44,13 @@ local function activator_rail_action_on(pos, node) end end end -mod.register_straight_rail("mcl_minecarts:activator_rail_v2_on", {"mcl_minecarts_rail_activator_powered.png"},{ +mod.register_curves_rail("mcl_minecarts:activator_rail_v2_on", { + "mcl_minecarts_rail_activator_powered.png", + "mcl_minecarts_rail_activator_curved_powered.png", + "mcl_minecarts_rail_activator_t_junction_powered.png", + "mcl_minecarts_rail_activator_t_junction_powered.png", + "mcl_minecarts_rail_activator_crossing_powered.png" +},{ _doc_items_create_entry = false, groups = { not_in_creative_inventory = 1, @@ -62,20 +66,6 @@ mod.register_straight_rail("mcl_minecarts:activator_rail_v2_on", {"mcl_minecarts -- Activate minecarts action_on = activator_rail_action_on, }, - - }, - mesecons_sloped = { - conductor = { - state = mesecon.state.on, - offstate = "mcl_minecarts:activator_rail_v2_sloped", - onstate = "mcl_minecarts:activator_rail_v2_on_sloped", - rules = mod.rail_rules_long, - }, - effector = { - -- Activate minecarts - action_on = activator_rail_action_on, - }, - }, _mcl_minecarts_on_enter = function(pos, cart) if cart.on_activate_by_rail then diff --git a/mods/ENTITIES/mcl_minecarts/rails/detector.lua b/mods/ENTITIES/mcl_minecarts/rails/detector.lua index 80ae596cc..fde472f10 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/detector.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/detector.lua @@ -6,7 +6,13 @@ local S = minetest.get_translator(modname) local rail_rules_short = mesecon.rules.pplate -- Detector rail (off) -mod.register_straight_rail("mcl_minecarts:detector_rail_v2",{"mcl_minecarts_rail_detector.png"},{ +mod.register_curves_rail("mcl_minecarts:detector_rail_v2",{ + "mcl_minecarts_rail_detector.png", + "mcl_minecarts_rail_detector_curved.png", + "mcl_minecarts_rail_detector_t_junction.png", + "mcl_minecarts_rail_detector_t_junction.png", + "mcl_minecarts_rail_detector_crossing.png" +},{ description = S("Detector Rail"), _tt_help = S("Track for minecarts").."\n"..S("Emits redstone power when a minecart is detected"), _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms."), @@ -19,7 +25,8 @@ mod.register_straight_rail("mcl_minecarts:detector_rail_v2",{"mcl_minecarts_rail }, _mcl_minecarts_on_enter = function(pos, cart) local node = minetest.get_node(pos) - node.name = "mcl_minecarts:detector_rail_v2_on" + local node_def = minetest.registered_nodes[node.name] + node.name = "mcl_minecarts:detector_rail_v2_on"..node_def._mcl_minecarts.suffix minetest.set_node( pos, node ) mesecon.receptor_on(pos) end, @@ -34,7 +41,13 @@ mod.register_straight_rail("mcl_minecarts:detector_rail_v2",{"mcl_minecarts_rail }) -- Detector rail (on) -mod.register_straight_rail("mcl_minecarts:detector_rail_v2_on",{"mcl_minecarts_rail_detector_powered.png"},{ +mod.register_curves_rail("mcl_minecarts:detector_rail_v2_on",{ + "mcl_minecarts_rail_detector_powered.png", + "mcl_minecarts_rail_detector_curved_powered.png", + "mcl_minecarts_rail_detector_t_junction_powered.png", + "mcl_minecarts_rail_detector_t_junction_powered.png", + "mcl_minecarts_rail_detector_crossing_powered.png" +},{ groups = { not_in_creative_inventory = 1, }, @@ -47,7 +60,8 @@ mod.register_straight_rail("mcl_minecarts:detector_rail_v2_on",{"mcl_minecarts_r }, _mcl_minecarts_on_leave = function(pos, cart) local node = minetest.get_node(pos) - node.name = "mcl_minecarts:detector_rail_v2" + local node_def = minetest.registered_nodes[node.name] + node.name = "mcl_minecarts:detector_rail_v2"..node_def._mcl_minecarts.suffix minetest.set_node( pos, node ) mesecon.receptor_off(pos) end, diff --git a/mods/ENTITIES/mcl_minecarts/rails/normal.lua b/mods/ENTITIES/mcl_minecarts/rails/normal.lua index dc8293bb1..58ff60758 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/normal.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/normal.lua @@ -5,7 +5,7 @@ local S = minetest.get_translator(modname) -- Normal rail mod.register_curves_rail("mcl_minecarts:rail_v2", { - "default_rail.png", + "default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_t_junction_on.png", diff --git a/mods/ENTITIES/mcl_minecarts/rails/powered.lua b/mods/ENTITIES/mcl_minecarts/rails/powered.lua index f63b091f6..3adb1f0c9 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/powered.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/powered.lua @@ -4,7 +4,13 @@ local mod = mcl_minecarts local S = minetest.get_translator(modname) -- Powered rail (off = brake mode) -mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_golden.png" },{ +mod.register_curves_rail("mcl_minecarts:golden_rail_v2",{ + "mcl_minecarts_rail_golden.png", + "mcl_minecarts_rail_golden_curved.png", + "mcl_minecarts_rail_golden_t_junction.png", + "mcl_minecarts_rail_golden_t_junction.png", + "mcl_minecarts_rail_golden_crossing.png" +},{ description = S("Powered Rail"), _tt_help = S("Track for minecarts").."\n"..S("Speed up when powered, slow down when not powered"), _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts."), @@ -21,14 +27,6 @@ mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_ rules = mod.rail_rules_long, }, }, - mesecons_sloped = { - conductor = { - state = mesecon.state.off, - offstate = "mcl_minecarts:golden_rail_v2_sloepd", - onstate = "mcl_minecarts:golden_rail_v2_on_sloped", - rules = mod.rail_rules_long, - }, - }, drop = "mcl_minecarts:golden_rail_v2", craft = { output = "mcl_minecarts:golden_rail_v2 6", @@ -41,7 +39,13 @@ mod.register_straight_rail("mcl_minecarts:golden_rail_v2",{ "mcl_minecarts_rail_ }) -- Powered rail (on = acceleration mode) -mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_rail_golden_powered.png" },{ +mod.register_curves_rail("mcl_minecarts:golden_rail_v2_on",{ + "mcl_minecarts_rail_golden_powered.png", + "mcl_minecarts_rail_golden_curved_powered.png", + "mcl_minecarts_rail_golden_t_junction_powered.png", + "mcl_minecarts_rail_golden_t_junction_powered.png", + "mcl_minecarts_rail_golden_crossing_powered.png", +},{ _doc_items_create_entry = false, _rail_acceleration = function(pos, staticdata) if staticdata.velocity ~= 0 then @@ -74,14 +78,6 @@ mod.register_straight_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_ra rules = mod.rail_rules_long, }, }, - mesecons_sloped = { - conductor = { - state = mesecon.state.on, - offstate = "mcl_minecarts:golden_rail_v2_sloped", - onstate = "mcl_minecarts:golden_rail_v2_on_sloped", - rules = mod.rail_rules_long, - }, - }, drop = "mcl_minecarts:golden_rail_v2", }) From aacc4b5d4a7bcd6f435a1cdedcee624e1048698d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 9 Nov 2024 15:44:37 -0600 Subject: [PATCH 163/195] Flip powered special T-junction textures to match regular rails --- ...ecarts_rail_activator_t_junction_powered.png | Bin 258 -> 495 bytes ...necarts_rail_detector_t_junction_powered.png | Bin 278 -> 517 bytes ...minecarts_rail_golden_t_junction_powered.png | Bin 254 -> 488 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/textures/mcl_minecarts_rail_activator_t_junction_powered.png b/textures/mcl_minecarts_rail_activator_t_junction_powered.png index 370364817daa82046d3b40c982bb937d487eb9ff..93b2dec6f55b339724226f12f0b9487c2f6e14d4 100644 GIT binary patch literal 495 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=Ijcz0ZJGL_=LCuX-`j29UYyTc%Q;B7h7A~ByVeFW#!H+ zzj>7jrlzI^Oia_6nVWcdJv=;2O-%m(|G(k>pCdqRj3q&S!3+-1Zlr-YN#5=*0#i5f z>Hs@T=EMU`0EG}n~@g*rT4977~7$M$>k9d_VwKKN(Zxg~r5=W+*L)%x0W zRQQIQi^wOzD=~QrCayZCt|vcX2t5+nY2t4ovUjgZR;<^VR*xFr1;+gW(^ytX$?%$W z-`IHGN^tkVlU1KYQ%c@gnzv}^OBXB?e3#4o`BBz{#v?&yT7GNT?@XInX0zM#D=HV3;tdmbgZgq$HN4S|t~y0x1R~10xe%LqlBy%Mc?&D`R6T zV@quV11kfAte=N8P&DM`r(~v8LNpjy>KYh_7@Am_7+IM>HRvs77YAz4fZI@#nVVW% fl9*e7Zi$JNfeFMCM^iOHpdJQKS3j3^P6a!n3|f}+S(R|xzxn_bY}S_ zd0RK}@_Kl9n3|YOXJ#&7VwzW(VB7P~45){(B*-tA!Qt7BG$5zJ)5S5Qf-yNkAc=v= znN6k5K)`qxm(d2+<=e&Cq@;}t*!=TK0>fq=IFS8rj@qJ_WtR>dcyQ*$nF9%j4jg#8 z<>?CVhL=oiaq7=kZ{OmhZ}5TDYNM2FXly*2`RR*~v2!xp qw*@`_|L>Qyc6yU!Ftx>q=QM*5vrI&xv<5fOF$|urelF{r5}E)~Syrw9 diff --git a/textures/mcl_minecarts_rail_detector_t_junction_powered.png b/textures/mcl_minecarts_rail_detector_t_junction_powered.png index 7638413cadda59dbedd1066b1de972e8f120f7aa..0ce8af4b85eee04bf3d3c4dc095ff2e8b0c28b03 100644 GIT binary patch literal 517 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=Ijcz0ZP~h_=LCuX-`j29UUE0Q`4GwpTaPg>CDW^%F4F3 zwn^UBomqbKDiiv8I$9c=vJ)c9ONw*zvI>}(GMJc}czOT-|Nm(2pDREk7)yfuf*Bm1 z-ADs*lDyqrL?+!oX$s`9mw5WRvcKTw6xEX2@?TOGC^X5_#W6(Ua_G6!VuuwtSS}T`}aS-+i-TCO_d`XgOkHnzJN0u8~Y&spbc zM{l#Ti;FIZzV_UnrzU!StHd{{*NZf@BObBcss3}A-`Ifp{KBU(Za~+mmbgZgq$HN4 zS|t~y0x1R~10xe%LqlBy%Mc?&D-%O2Lj!FC11kfA30nC}Q!>*kAsP%Ubq$O| z3{9*|jI2y7AsX}+vx@^YXuxeK$;?eHE=kNSK)1xm%E%0&XTz${MxY)BPgg&ebxsLQ E0MHqzv;Y7A delta 250 zcmV0J*UPsE&~ZRsaA107*qoM6N<$f{J8g AYybcN diff --git a/textures/mcl_minecarts_rail_golden_t_junction_powered.png b/textures/mcl_minecarts_rail_golden_t_junction_powered.png index 6a4af02094c9c0bc4faa0ad357879e720e1cd23b..142d5784dbee2cdbc6d6a7bdb38c7e3fe5c14434 100644 GIT binary patch literal 488 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFvNcITwWnidMV_;}#VPNMqoY$3?^77&Vry%gR{59<)#HpOw*Z}=T#>B|Noyg&h-aS4`WG?UoeBivm0q3PLj8~3%|rJhMPbR zdx@v7EBgy>PEie3?t^7nK%o{-7sn8e>&XcMNeoP`!fYJQp*@X?GDiZkQWEmE&&kPL zwD8CQwWul6jC_?F8#f1Le|3Av#mv@rtgNl*BNrRnyaftrA9)?v@+YjACEIaO`N)kY zI~Is!v@X{v8k1Tp|*j6m4U&n z_TR5jH00)|WTsU@G#FUw8W@Kdnpl|_S(#WuH0Uj67YAz4fZI@#nVVW%l9*e7W{C;V YcvFa;cL`g0s@x<1JtKAgl7zN00001bW%=J06^y0W&i*He@R3^RCobnU=U&e0!1J;f-+db z+{?=wg9RBl!xk->XsD#bz-8vu7@DNm094E(nzTWgfq_SeF Date: Sat, 9 Nov 2024 20:05:48 -0600 Subject: [PATCH 164/195] Fix crash when train cars leave track --- mods/ENTITIES/mcl_minecarts/movement.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 417660e00..f7f1fd817 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -121,6 +121,7 @@ end local function detach_minecart(staticdata) handle_cart_leave(staticdata, staticdata.connected_at, staticdata.dir) staticdata.connected_at = nil + mod.break_train_at(staticdata) local luaentity = mcl_util.get_luaentity_from_uuid(staticdata.uuid) if luaentity then From bf3e9190b360f85a8c593d9cf0057a1b021c44dd Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 10 Nov 2024 07:11:12 -0600 Subject: [PATCH 165/195] Fix syntax error from rebase, fix cart tilt, change cart speed to 4 blocks/second and make into a setting, make minecart settings compatibily with upcoming dynamic rules PR (#4662) --- mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua | 8 ++++++-- mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua | 4 ++-- mods/ENTITIES/mcl_minecarts/functions.lua | 6 +++--- mods/MAPGEN/tsm_railcorridors/gameconfig.lua | 1 + 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua b/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua index 13693b733..bd50b023d 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua @@ -1,6 +1,8 @@ local modname = minetest.get_current_modname() local S = minetest.get_translator(modname) +local FURNACE_CART_SPEED = {tonumber(minetest.settings:get("mcl_minecarts_furnace_speed") or 4)} + -- Minecart with Furnace mcl_minecarts.register_minecart({ itemstring = "mcl_minecarts:furnace_minecart", @@ -73,8 +75,10 @@ mcl_minecarts.register_minecart({ -- Update furnace stuff if (staticdata.fueltime or 0) > 0 then - if staticdata.velocity < 0.25 then - staticdata.velocity = 0.25 + for car in mcl_minecarts.train_cars(staticdata) do + if car.velocity < FURNACE_CART_SPEED[1] - 0.1 then -- Slightly less to allow train cars to maintain spacing + car.velocity = FURNACE_CART_SPEED[1] + end end staticdata.fueltime = (staticdata.fueltime or dtime) - dtime diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua b/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua index 022fc05f7..9e6defdad 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/with_hopper.lua @@ -3,9 +3,9 @@ local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts local S = minetest.get_translator(modname) -local LOGGING_ON = minetest.settings:get_bool("mcl_logging_minecarts", false) +local LOGGING_ON = {minetest.settings:get_bool("mcl_logging_minecarts", false)} local function mcl_log(message) - if LOGGING_ON then + if LOGGING_ON[1] then mcl_util.mcl_log(message, "[Minecarts]", true) end end diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 97933f2ef..0e511c224 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -441,7 +441,7 @@ end local _2_pi = math.pi * 2 local _half_pi = math.pi * 0.5 -local _quart_pi = math.pi * 0.5 +local _quart_pi = math.pi * 0.25 local pi = math.pi local rot_debug = {} function mod.update_cart_orientation(self) @@ -469,9 +469,9 @@ function mod.update_cart_orientation(self) end -- Forward/backwards tilt (pitch) - if dir.y < 0 then + if dir.y > 0 then rot.x = -_quart_pi - elseif dir.y > 0 then + elseif dir.y < 0 then rot.x = _quart_pi else rot.x = 0 diff --git a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua index b5aedec0b..1cfeb8368 100644 --- a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua +++ b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua @@ -61,6 +61,7 @@ tsm_railcorridors.carts = { "mcl_minecarts:minecart", "mcl_minecarts:minecart", "mcl_minecarts:chest_minecart", "mcl_minecarts:chest_minecart", "mcl_minecarts:tnt_minecart" +} local has_loot = { ["mcl_minecarts:chest_minecart"] = true, ["mcl_minecarts:hopper_minceart"] = true, From dd9edcb827526ef6d97e15348d09e4439ab34223 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 10 Nov 2024 07:36:02 -0600 Subject: [PATCH 166/195] Change cart punching to impulse on velocity instead of acceleration, make punch impulse larger --- mods/ENTITIES/mcl_minecarts/carts.lua | 15 ++++++--- mods/ENTITIES/mcl_minecarts/movement.lua | 41 +++++++++++++----------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 39770ebc5..61cff7bb8 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -266,14 +266,21 @@ function DEFAULT_CART_DEF:on_punch(puncher, time_from_last_punch, tool_capabilit end local controls = staticdata.controls or {} - local impulse = vector.multiply(dir, damage * 20) - local accel = vector.dot(staticdata.dir, impulse) - if accel < 0 and staticdata.velocity == 0 then + dir.y = 0 + dir = vector.normalize(dir) + local impulse = vector.dot(staticdata.dir, vector.multiply(dir, damage * 4)) + minetest.log(dump({ + dir = dir, + dir_len = vector.length(dir), + damage = damage, + impulse = impulse, + })) + if impulse < 0 and staticdata.velocity == 0 then mod.reverse_direction(staticdata) + impulse = -impulse end controls.impulse = impulse - --print("uuid="..self._uuid..", controls="..dump(controls)) staticdata.controls = controls end function DEFAULT_CART_DEF:on_step(dtime) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index f7f1fd817..44ab1c535 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -286,9 +286,6 @@ local function calculate_acceleration(staticdata) acceleration = 4 elseif (ctrl.brake or 0) > time_active then acceleration = -1.5 - elseif ctrl.impulse then - acceleration = vector.dot(staticdata.dir, ctrl.impulse) - ctrl.impulse = nil elseif (staticdata.fueltime or 0) > 0 and staticdata.velocity <= 4 then acceleration = 0.6 elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or SPEED_MAX ) then @@ -320,8 +317,24 @@ local function do_movement_step(staticdata, dtime) local x_0 = staticdata.distance or 0 local remaining_in_block = 1 - x_0 - -- Calculate acceleration + -- Apply velocity impulse local v_0 = staticdata.velocity or 0 + local ctrl = staticdata.controls or {} + if ctrl.impulse then + local impulse = ctrl.impulse + ctrl.impulse = nil + + local old_v_0 = v_0 + local new_v_0 = v_0 + impulse + if new_v_0 > SPEED_MAX then + new_v_0 = SPEED_MAX + elseif new_v_0 < 0.025 then + new_v_0 = 0 + end + v_0 = new_v_0 + end + + -- Calculate acceleration local a = 0 if staticdata.ahead or staticdata.behind then -- Calculate acceleration of the entire train @@ -385,18 +398,17 @@ local function do_movement_step(staticdata, dtime) end -- Truncate timestep to prevent v_1 from being larger that speed_max - local v_max = SPEED_MAX - if (v_0 < v_max) and ( v_0 + a * timestep > v_max) then - timestep = ( v_max - v_0 ) / a + if (v_0 < SPEED_MAX) and ( v_0 + a * timestep > SPEED_MAX) then + timestep = ( SPEED_MAX - v_0 ) / a end -- Prevent infinite loops if timestep <= 0 then return 0 end - -- Calculate v_1 taking v_max into account + -- Calculate v_1 taking SPEED_MAX into account local v_1 = v_0 + a * timestep - if v_1 > v_max then - v_1 = v_max + if v_1 > SPEED_MAX then + v_1 = SPEED_MAX elseif v_1 < 0.025 then v_1 = 0 end @@ -496,15 +508,6 @@ end function submod.do_movement( staticdata, dtime ) assert(staticdata) - -- Allow the carts to be delay for the rest of the world to react before moving again - --[[ - if ( staticdata.delay or 0 ) > dtime then - staticdata.delay = staticdata.delay - dtime - return - else - staticdata.delay = 0 - end]] - -- Break long movements at block boundaries to make it -- it impossible to jump across gaps due to server lag -- causing large timesteps From 6f5760000e693c1bdd16e12dee257beb45a24e79 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 10 Nov 2024 12:01:42 -0600 Subject: [PATCH 167/195] Address review comments --- mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua b/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua index bd50b023d..9bc09774f 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/with_furnace.lua @@ -1,7 +1,7 @@ local modname = minetest.get_current_modname() local S = minetest.get_translator(modname) -local FURNACE_CART_SPEED = {tonumber(minetest.settings:get("mcl_minecarts_furnace_speed") or 4)} +local FURNACE_CART_SPEED = tonumber(minetest.settings:get("mcl_minecarts_furnace_speed")) or 4 -- Minecart with Furnace mcl_minecarts.register_minecart({ @@ -76,8 +76,8 @@ mcl_minecarts.register_minecart({ -- Update furnace stuff if (staticdata.fueltime or 0) > 0 then for car in mcl_minecarts.train_cars(staticdata) do - if car.velocity < FURNACE_CART_SPEED[1] - 0.1 then -- Slightly less to allow train cars to maintain spacing - car.velocity = FURNACE_CART_SPEED[1] + if car.velocity < FURNACE_CART_SPEED - 0.1 then -- Slightly less to allow train cars to maintain spacing + car.velocity = FURNACE_CART_SPEED end end From ddb70e5ebf34e9cc98a9cede7e8b74ffd9f5cd80 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 12 Nov 2024 06:22:08 -0600 Subject: [PATCH 168/195] Increase off-rail friction, make off-rail carts shake, remove debug print --- mods/ENTITIES/mcl_minecarts/carts.lua | 6 ------ mods/ENTITIES/mcl_minecarts/init.lua | 1 + mods/ENTITIES/mcl_minecarts/movement.lua | 17 +++++++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 61cff7bb8..df77f6cf7 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -269,12 +269,6 @@ function DEFAULT_CART_DEF:on_punch(puncher, time_from_last_punch, tool_capabilit dir.y = 0 dir = vector.normalize(dir) local impulse = vector.dot(staticdata.dir, vector.multiply(dir, damage * 4)) - minetest.log(dump({ - dir = dir, - dir_len = vector.length(dir), - damage = damage, - impulse = impulse, - })) if impulse < 0 and staticdata.velocity == 0 then mod.reverse_direction(staticdata) impulse = -impulse diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 081e78314..55960e296 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -7,6 +7,7 @@ mcl_minecarts.modpath = modpath -- Constants mod.SPEED_MAX = 10 mod.FRICTION = 0.4 +mod.OFF_RAIL_FRICTION = 1.2 mod.MAX_TRAIN_LENGTH = 4 mod.CART_BLOCK_SIZE = 64 mod.PASSENGER_ATTACH_POSITION = vector.new(0, -1.75, 0) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 44ab1c535..aa1025a93 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -16,7 +16,8 @@ if minetest.get_modpath("mcl_physics") then elseif minetest.get_modpath("vl_physics") then env_physics = vl_physics end -local FRICTION = mcl_minecarts.FRICTION +local FRICTION = mod.FRICTION +local OFF_RAIL_FRICTION = mod.OFF_RAIL_FRICTION local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH local SPEED_MAX = mod.SPEED_MAX local train_length = mod.train_length @@ -524,18 +525,20 @@ function submod.do_movement( staticdata, dtime ) end end +local _half_pi = math.pi * 0.5 function submod.do_detached_movement(self, dtime) local staticdata = self._staticdata -- Make sure the object is still valid before trying to move it - if not self.object or not self.object:get_pos() then return end + local velocity = self.object:get_velocity() + if not self.object or not velocity then return end -- Apply physics if env_physics then env_physics.apply_entity_environmental_physics(self) else -- Simple physics - local friction = self.object:get_velocity() or vector.zero() + local friction = velocity or vector.zero() friction.y = 0 local accel = vector.new(0,-9.81,0) -- gravity @@ -543,15 +546,17 @@ function submod.do_detached_movement(self, dtime) -- Don't apply friction in the air local pos_rounded = vector.round(self.object:get_pos()) if minetest.get_node(vector.offset(pos_rounded,0,-1,0)).name ~= "air" then - accel = vector.add(accel, vector.multiply(friction,-0.9)) + accel = vector.add(accel, vector.multiply(friction,-OFF_RAIL_FRICTION)) end self.object:set_acceleration(accel) end - -- Reset pitch + -- Shake the cart (also resets pitch) local rot = self.object:get_rotation() - rot.y = 0 + local shake_amount = 0.05 * vector.length(velocity) + rot.x = (math.random() - 0.5) * shake_amount + rot.z = (math.random() - 0.5) * shake_amount self.object:set_rotation(rot) local away = vector_away_from_players(self, staticdata) From 8881048a4cf736b4b567c11e87da75c9bf1c9a32 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 13 Nov 2024 20:48:58 -0600 Subject: [PATCH 169/195] Remove debug minetest.log() --- mods/ENTITIES/mcl_minecarts/rails.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index aa4cc2959..813d7da67 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -248,7 +248,6 @@ local function make_mesecons(base_name, suffix, base_mesecons) end end - minetest.log("mesecons for "..base_name..suffix.." is "..dump(mesecons)) return mesecons end From 3cc8a8474ddbba93d625558352f49905d728b2e7 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 26 Dec 2024 15:45:13 -0600 Subject: [PATCH 170/195] Fix rebase duplicating code --- mods/CORE/mcl_util/init.lua | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index fe195108b..56a4aaa3d 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -810,16 +810,6 @@ end function mcl_util.get_luaentity_from_uuid(uuid) return minetest.luaentities[ mcl_util.get_active_object_id_from_uuid(uuid) ] end -function mcl_util.gen_uuid() - -- Generate a random 128-bit ID that can be assumed to be unique - -- To have a 1% chance of a collision, there would have to be 1.6x10^76 IDs generated - -- https://en.wikipedia.org/wiki/Birthday_problem#Probability_table - local u = {} - for i = 1,16 do - u[#u + 1] = string.format("%02X",math.random(1,255)) - end - return table.concat(u) -end function mcl_util.assign_uuid(obj) assert(obj) From 8118495c8044300e51a0dea2d1336259692865b2 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 26 Dec 2024 18:15:21 -0600 Subject: [PATCH 171/195] Add guard to prevent crash and log when this happens --- mods/ENTITIES/mcl_minecarts/carts.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index df77f6cf7..64a862b8a 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -294,7 +294,10 @@ function DEFAULT_CART_DEF:on_step(dtime) end -- Remove superceded entities - if staticdata.seq and self._seq < staticdata.seq then + if staticdata.seq and (self._seq or -1) < staticdata.seq then + if not self._seq then + core.log("warning", "Removing minecart entity missing sequence number") + end --print("removing cart #"..staticdata.uuid.." with sequence number mismatch") self.object:remove() self._removed = true From a694675b875fa22f7503c72837640aafb61dd8c8 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 27 Dec 2024 06:26:39 -0600 Subject: [PATCH 172/195] Remove stray line --- mods/ENTITIES/mcl_minecarts/functions.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 0e511c224..d5304448b 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -265,7 +265,6 @@ local function is_rail_end_connected(pos, dir) end local function bend_straight_rail(pos, towards) - dir = CONNECTIONS[i] local node = force_get_node(pos) local nodedef = minetest.registered_nodes[node.name] From bf3b98021fa6f780da90699b636ab2062c025eec Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 28 Dec 2024 16:35:48 -0600 Subject: [PATCH 173/195] Fix conversion of cart inventories to mod storage without duplicating items --- mods/ENTITIES/mcl_minecarts/carts.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index 64a862b8a..fb3b7f995 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -178,6 +178,13 @@ function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s) local data = minetest.deserialize(staticdata) or {} if not data.uuid then data.uuid = mcl_util.assign_uuid(self.object) + + if data._items then + data.inventory = data._items + data._items = nil + data._inv_id = nil + data._inv_size = nil + end end self._seq = data.seq or 1 @@ -283,6 +290,9 @@ function DEFAULT_CART_DEF:on_step(dtime) staticdata = make_staticdata() self._staticdata = staticdata end + if self._items then + self._items = nil + end -- Update entity position local pos = mod.get_cart_position(staticdata) From e28b55c16a7255f5bb30f2aecc333e131b9d6ac6 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 28 Dec 2024 17:04:05 -0600 Subject: [PATCH 174/195] Replace linear cart fill with mcl_loot.fill_inventory() using a fake inventory, remove debug print --- mods/MAPGEN/tsm_railcorridors/gameconfig.lua | 26 ++++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua index 1cfeb8368..177ddef9d 100644 --- a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua +++ b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua @@ -88,15 +88,25 @@ function tsm_railcorridors.create_cart_staticdata(entity_id, pos, pr) if cartdata and has_loot[entity_id] then local items = tsm_railcorridors.get_treasures(pr) - -- TODO: determine if we should convert to use mcl_loot - -- mcl_loot.fill_inventory(inv, "main", items, pr_carts) - -- Convert from ItemStack to itemstrings - for k,item in pairs(items) do - items[k] = item:to_string() - end - cartdata.inventory = items + local size = core.registered_entities[entity_id]._inv_size + local inventory = {} + for i = 1,size do inventory[i] = "" end + cartdata.inventory = inventory + + -- Fill a fake inventory using mcl_loot + local fake_inv = { + get_size = function(self) + return size + end, + get_stack = function(self, _, i) + return ItemStack(inventory[i]) + end, + set_stack = function(self, _, i, stack) + inventory[i] = stack:to_string() + end, + } + mcl_loot.fill_inventory(fake_inv, "main", items, pr_carts) - print("cartdata = "..dump(cartdata)) save_cart_data(uuid) end From 6559bc23f91d4178cb2a6e027d4391088b97cf01 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 28 Dec 2024 18:34:27 -0600 Subject: [PATCH 175/195] Fix orientation of carts on slopes --- mods/ENTITIES/mcl_minecarts/functions.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index d5304448b..1305a87a1 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -469,9 +469,9 @@ function mod.update_cart_orientation(self) -- Forward/backwards tilt (pitch) if dir.y > 0 then - rot.x = -_quart_pi - elseif dir.y < 0 then rot.x = _quart_pi + elseif dir.y < 0 then + rot.x = -_quart_pi else rot.x = 0 end @@ -479,9 +479,6 @@ function mod.update_cart_orientation(self) if ( staticdata.rot_adjust or 0 ) < 0.01 then rot.x = -rot.x end - if dir.z ~= 0 then - rot.x = -rot.x - end rot.y = (rot.y + _half_pi) % _2_pi self.object:set_rotation(rot) From b627d717500dca6c2aa577abee855666da1dcde2 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 28 Dec 2024 18:56:09 -0600 Subject: [PATCH 176/195] Allow curved special rail conversion without dropping as item --- mods/ENTITIES/mcl_minecarts/rails.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 813d7da67..4ce80ff0d 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -429,6 +429,12 @@ end local CURVY_RAILS_MAP = { ["mcl_minecarts:rail"] = "mcl_minecarts:rail_v2", + ["mcl_minecarts:golden_rail"] = "mcl_minecarts:golden_rail_v2", + ["mcl_minecarts:golden_rail_on"] = "mcl_minecarts:golden_rail_v2_on", + ["mcl_minecarts:activator_rail"] = "mcl_minecarts:activator_rail_v2", + ["mcl_minecarts:activator_rail_on"] = "mcl_minecarts:activator_rail_v2_on", + ["mcl_minecarts:detector_rail"] = "mcl_minecarts:detector_rail_v2", + ["mcl_minecarts:detector_rail_on"] = "mcl_minecarts:detector_rail_v2_on", } local function convert_legacy_curvy_rails(pos, node) node.name = CURVY_RAILS_MAP[node.name] @@ -449,12 +455,6 @@ for old,new in pairs(CURVY_RAILS_MAP) do vl_legacy.register_item_conversion(old, new) end local STRAIGHT_RAILS_MAP ={ - ["mcl_minecarts:golden_rail"] = "mcl_minecarts:golden_rail_v2", - ["mcl_minecarts:golden_rail_on"] = "mcl_minecarts:golden_rail_v2_on", - ["mcl_minecarts:activator_rail"] = "mcl_minecarts:activator_rail_v2", - ["mcl_minecarts:activator_rail_on"] = "mcl_minecarts:activator_rail_v2_on", - ["mcl_minecarts:detector_rail"] = "mcl_minecarts:detector_rail_v2", - ["mcl_minecarts:detector_rail_on"] = "mcl_minecarts:detector_rail_v2_on", } local function convert_legacy_straight_rail(pos, node) node.name = STRAIGHT_RAILS_MAP[node.name] From 929d7a64920e1089bc89743c89df920e8c8d1065 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 28 Dec 2024 19:30:51 -0600 Subject: [PATCH 177/195] Make trains experimental behind a setting --- mods/ENTITIES/mcl_minecarts/movement.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index aa1025a93..9a04e2aad 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -3,6 +3,7 @@ local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts local S = minetest.get_translator(modname) local submod = {} +local ENABLE_TRAINS = core.settings:get_bool("mcl_minecarts_experimental_trains",false) -- Constants local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", "Minecart Debug") @@ -181,7 +182,7 @@ local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) local m2 = cart2_staticdata.mass --print("u1="..tostring(u1)..",u2="..tostring(u2)) - if u2 == 0 and u1 < 4 and train_length(cart1_staticdata) < MAX_TRAIN_LENGTH then + if ENABLE_TRAINS and u2 == 0 and u1 < 4 and train_length(cart1_staticdata) < MAX_TRAIN_LENGTH then link_cart_ahead(cart1_staticdata, cart2_staticdata) cart2_staticdata.dir = mcl_minecarts.get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) cart2_staticdata.velocity = cart1_staticdata.velocity From 9b647b082370d54d9952169f2266e6400d2a6caa Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sat, 28 Dec 2024 20:02:52 -0600 Subject: [PATCH 178/195] Add curve->tee and tee->cross conversion logic, fix typo --- mods/ENTITIES/mcl_minecarts/functions.lua | 19 +++++++++++++++---- mods/ENTITIES/mcl_minecarts/rails.lua | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 1305a87a1..d1099c6fd 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -283,7 +283,6 @@ local function bend_straight_rail(pos, towards) local dir2_connected = is_rail_end_connected(pos + dir2, dir1) if dir1_connected and dir2_connected then return end - -- TODO: bend the rail local connections = { vector.direction(pos, towards), } @@ -307,8 +306,6 @@ local function bend_straight_rail(pos, towards) end local function update_rail_connections(pos, opt) - local ignore_neighbor_connections = opt and opt.ignore_neighbor_connections - local node = minetest.get_node(pos) local nodedef = minetest.registered_nodes[node.name] if not nodedef or not nodedef._mcl_minecarts then return end @@ -333,7 +330,7 @@ local function update_rail_connections(pos, opt) for i = 1,#CONNECTIONS do local dir = CONNECTIONS[i] local neighbor = vector.add(pos, dir) - make_sloped_if_straight( vector.offset(neighbor, 0, -1, 0), dir ) + make_sloped_if_straight(vector.offset(neighbor, 0, -1, 0), dir) end apply_connection_rules(node, nodedef, pos, rules, connections) @@ -350,6 +347,20 @@ local function update_rail_connections(pos, opt) end end + -- Check if the open end of this rail runs into a corner or a tee and convert that node into a tee or a cross + local neighbors = {} + for i=1,#CONNECTIONS do + local dir = CONNECTIONS[i] + if is_connection(pos, dir) then + local other_pos = pos - dir + local other_node = core.get_node(other_pos) + local other_node_def = core.registered_nodes[other_node.name] + local railtype = get_path(other_node_def, "_mcl_minecarts","railtype") + if (not opt or opt.convert_neighbors ~= false) and railtype == "corner" or railtype == "tee" then + update_rail_connections(other_pos, {convert_neighbors = false}) + end + end + end end mod.update_rail_connections = update_rail_connections diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 4ce80ff0d..3163763f5 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -20,7 +20,7 @@ local south = mod.south local east = mod.east local west = mod.west ---- Rail direction Handleres +--- Rail direction Handlers local function rail_dir_straight(pos, dir, node) dir = vector.new(dir) dir.y = 0 From bef17e5401d868dc4935c2bd0274713afc6b8050 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 06:54:36 -0600 Subject: [PATCH 179/195] Patch initial cart direction when placing --- mods/ENTITIES/mcl_minecarts/carts.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index fb3b7f995..afd23924b 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -401,9 +401,20 @@ function mod.place_minecart(itemstack, pointed_thing, placer) if railpos then spawn_pos = railpos node = minetest.get_node(railpos) - cart_dir = mcl_minecarts.get_rail_direction(railpos, vector.new(1,0,0)) + + -- Try two orientations, and select the second if the first is at an angle + cart_dir1 = mcl_minecarts.get_rail_direction(railpos, vector.new( 1,0,0)) + cart_dir2 = mcl_minecarts.get_rail_direction(railpos, vector.new(-1,0,0)) + if vector.length(cart_dir1) <= 1 then + cart_dir = cart_dir1 + else + cart_dir = cart_dir2 + end end + -- Make sure to always go down slopes + if cart_dir.y > 0 then cart_dir = -cart_dir end + local entity_id = entity_mapping[itemstack:get_name()] local uuid = create_minecart(entity_id, railpos, cart_dir) From bf9ad1919e7144d573288b4455a955f4847faa68 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 07:04:02 -0600 Subject: [PATCH 180/195] Prevent players getting in the same cart as a mob, don't allow mobs already in a cart to be moved to another one --- mods/ENTITIES/mcl_minecarts/carts/minecart.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua index 600eedc5a..004426d80 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua @@ -22,9 +22,13 @@ function mod.attach_driver(cart, player) -- Make sure we have a player if not player or not player:is_player() then return end + -- Prevent more than one player getting in the cart local player_name = player:get_player_name() if cart._driver or player:get_player_control().sneak then return end + -- Prevent getting into a cart that already has a passenger + if cart._passenger then return end + -- Update cart information cart._driver = player_name cart._start_pos = cart.object:get_pos() @@ -82,7 +86,7 @@ mod.register_minecart({ local mobsnear = minetest.get_objects_inside_radius(self.object:get_pos(), 1.3) for n=1, #mobsnear do local mob = mobsnear[n] - if mob then + if mob and not mob:get_attach() then local entity = mob:get_luaentity() if entity and entity.is_mob then self._passenger = entity From 48c380ca0b48ab1ac14a9e2821f6baf995d0ee70 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 07:04:39 -0600 Subject: [PATCH 181/195] Make sure to clear _passenger after expelling mobs --- mods/ENTITIES/mcl_minecarts/carts/minecart.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua index 004426d80..a155c94b5 100644 --- a/mods/ENTITIES/mcl_minecarts/carts/minecart.lua +++ b/mods/ENTITIES/mcl_minecarts/carts/minecart.lua @@ -13,6 +13,7 @@ local function activate_normal_minecart(self) if self._passenger then local mob = self._passenger.object mob:set_detach() + self._passenger = nil end end From 31066caf06f481ab8b97ec64c3ecfc95a0c0960d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 07:35:17 -0600 Subject: [PATCH 182/195] Fix cart reversal/wrong direction movement on collisions --- mods/ENTITIES/mcl_minecarts/movement.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 9a04e2aad..47e22f99d 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -181,7 +181,6 @@ local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) local m1 = cart1_staticdata.mass local m2 = cart2_staticdata.mass - --print("u1="..tostring(u1)..",u2="..tostring(u2)) if ENABLE_TRAINS and u2 == 0 and u1 < 4 and train_length(cart1_staticdata) < MAX_TRAIN_LENGTH then link_cart_ahead(cart1_staticdata, cart2_staticdata) cart2_staticdata.dir = mcl_minecarts.get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) @@ -189,6 +188,15 @@ local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) return end + -- Reverse direction of the second cart if it is pointing in the wrong direction for this collision + local rel = vector.direction(cart1_staticdata.connected_at, cart2_staticdata.connected_at) + local dir2 = cart2_staticdata.dir + local col_dir = vector.dot(rel, dir2) + if col_dir < 0 then + cart2_staticdata.dir = -dir2 + u2 = -u2 + end + -- Calculate new velocities according to https://en.wikipedia.org/wiki/Elastic_collision#One-dimensional_Newtonian local c1 = m1 + m2 local d = m1 - m2 @@ -197,9 +205,6 @@ local function handle_cart_collision(cart1_staticdata, prev_pos, next_dir) cart1_staticdata.velocity = v1 cart2_staticdata.velocity = v2 - - -- Force the other cart to move the same direction this one was - cart2_staticdata.dir = mcl_minecarts.get_rail_direction(cart2_staticdata.connected_at, cart1_staticdata.dir) end local function vector_away_from_players(cart, staticdata) From cacc9d384fb1b29b2bf061ced01c8a0490b84df5 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 09:17:35 -0600 Subject: [PATCH 183/195] Handle edge case in mcl_minecarts.reverse_cart_direction() that lead to strange punching and pushing behavior --- mods/ENTITIES/mcl_minecarts/functions.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index d1099c6fd..026f1ffc9 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -504,6 +504,10 @@ function mod.get_cart_position(cart_staticdata) end function mod.reverse_cart_direction(staticdata) + if staticdata.distance == 0 then + staticdata.dir = -staticdata.dir + return + end -- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at local next_dir = -staticdata.dir From 6898675e054f3356687915e3a509ae7af075d200 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 10:26:31 -0600 Subject: [PATCH 184/195] Limit detached cart vertical boost to no more than what is needed to get over the lip of rails --- mods/ENTITIES/mcl_minecarts/movement.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 47e22f99d..519a7b793 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -573,7 +573,9 @@ function submod.do_detached_movement(self, dtime) -- Boost the minecart vertically a bit to get over the edge of rails and things like carpets local boost = vector.offset(vector.multiply(vector.normalize(away), 0.1), 0, 0.07, 0) -- 1/16th + 0.0075 local pos = self.object:get_pos() - self.object:set_pos(vector.add(pos,boost)) + if pos.y - math.floor(pos.y) < boost.y then + self.object:set_pos(vector.add(pos,boost)) + end end -- Try to reconnect to rail From 42fe5c4c858c29d20354707b9937018f0fbecdf2 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 10:40:28 -0600 Subject: [PATCH 185/195] Reduce repulsion of carts off rails --- mods/ENTITIES/mcl_minecarts/movement.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 519a7b793..7f2bb1104 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -567,7 +567,7 @@ function submod.do_detached_movement(self, dtime) local away = vector_away_from_players(self, staticdata) if away then - local v = self.object:get_velocity() + local v = self.object:get_velocity() * 0.25 self.object:set_velocity(v - away) -- Boost the minecart vertically a bit to get over the edge of rails and things like carpets From 3b63b1eec36b417e2be470a890111cca429b79f9 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 11:53:20 -0600 Subject: [PATCH 186/195] Swap push/pull for straight hoppers --- mods/ITEMS/mcl_hoppers/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index 995703e40..e604ec28c 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -298,14 +298,14 @@ local def_hopper = { -- Only pull to containers if cart and cart.groups and (cart.groups.container or 0) ~= 0 then cart:add_node_watch(pos) - hopper_push_to_mc(cart, pos, 5) + hopper_pull_from_mc(cart, pos, 5) end end, _mcl_minecarts_on_enter_above = function(pos, cart, next_dir) -- Only push to containers if cart and cart.groups and (cart.groups.container or 0) ~= 0 then cart:add_node_watch(pos) - hopper_pull_from_mc(cart, pos, 5) + hopper_push_to_mc(cart, pos, 5) end end, _mcl_minecarts_on_leave_above = function(pos, cart, next_dir) From aef68d34fd457eca7bc5e27b1a8862fcd873a66f Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 11:55:55 -0600 Subject: [PATCH 187/195] Address review comment and adjust multiplier --- mods/ENTITIES/mcl_minecarts/movement.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index 7f2bb1104..d046d896e 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -567,8 +567,8 @@ function submod.do_detached_movement(self, dtime) local away = vector_away_from_players(self, staticdata) if away then - local v = self.object:get_velocity() * 0.25 - self.object:set_velocity(v - away) + local v = self.object:get_velocity() + self.object:set_velocity((v - away)*0.65) -- Boost the minecart vertically a bit to get over the edge of rails and things like carpets local boost = vector.offset(vector.multiply(vector.normalize(away), 0.1), 0, 0.07, 0) -- 1/16th + 0.0075 From c97e2f77e7de14055f5bd289525adb7ed1381ceb Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 12:11:31 -0600 Subject: [PATCH 188/195] Add comments to clarify _mcl_minecarts_on_enter_*() callbacks --- mods/ITEMS/mcl_hoppers/init.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mods/ITEMS/mcl_hoppers/init.lua b/mods/ITEMS/mcl_hoppers/init.lua index e604ec28c..b120f22b5 100644 --- a/mods/ITEMS/mcl_hoppers/init.lua +++ b/mods/ITEMS/mcl_hoppers/init.lua @@ -295,6 +295,8 @@ local def_hopper = { " takes stuff from mcl_hoppers at " .. minetest.pos_to_string(pos)) end, _mcl_minecarts_on_enter_below = function(pos, cart, next_dir) + -- Hopper is below minecart + -- Only pull to containers if cart and cart.groups and (cart.groups.container or 0) ~= 0 then cart:add_node_watch(pos) @@ -302,6 +304,8 @@ local def_hopper = { end end, _mcl_minecarts_on_enter_above = function(pos, cart, next_dir) + -- Hopper is above minecart + -- Only push to containers if cart and cart.groups and (cart.groups.container or 0) ~= 0 then cart:add_node_watch(pos) @@ -540,6 +544,8 @@ local def_hopper_side = { sounds = mcl_sounds.node_sound_metal_defaults(), _mcl_minecarts_on_enter_below = function(pos, cart, next_dir) + -- Hopper is below minecart + -- Only push to containers if cart and cart.groups and (cart.groups.container or 0) ~= 0 then cart:add_node_watch(pos) @@ -552,6 +558,8 @@ local def_hopper_side = { cart:remove_node_watch(pos) end, _mcl_minecarts_on_enter_side = function(pos, cart, next_dir, rail_pos) + -- Hopper is to the side of the minecart + if not cart then return end -- Only try to push to minecarts when the spout position is pointed at the rail From 363187b0d6864910ac38fdae4dc9eb376ddfe83c Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 16:07:43 -0600 Subject: [PATCH 189/195] Prevent glitching into solid blocks at the end of rail --- mods/ENTITIES/mcl_minecarts/movement.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index d046d896e..d7330086f 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -381,6 +381,16 @@ local function do_movement_step(staticdata, dtime) -- Not moving if a == 0 and v_0 == 0 then return 0 end + -- Prevent movement into solid blocks + if staticdata.distance == 0 then + local next_node = core.get_node(staticdata.connected_at + staticdata.dir) + local next_node_def = core.registered_nodes[next_node.name] + if next_node_def and next_node_def.groups and (next_node_def.groups.solid or next_node_def.groups.stair) then + reverse_direction(staticdata) + return 0 + end + end + -- Movement equation with acceleration: x_1 = x_0 + v_0 * t + 0.5 * a * t*t local timestep local stops_in_block = false From 46ea9e98ccc86f62eff7d6ad58e86f967928f35d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 16:20:16 -0600 Subject: [PATCH 190/195] Guard against undefined nodes --- mods/ENTITIES/mcl_minecarts/movement.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index d7330086f..d20820326 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -385,7 +385,7 @@ local function do_movement_step(staticdata, dtime) if staticdata.distance == 0 then local next_node = core.get_node(staticdata.connected_at + staticdata.dir) local next_node_def = core.registered_nodes[next_node.name] - if next_node_def and next_node_def.groups and (next_node_def.groups.solid or next_node_def.groups.stair) then + if not next_node_def or next_node_def.groups and (next_node_def.groups.solid or next_node_def.groups.stair) then reverse_direction(staticdata) return 0 end From 0dbf3fcd6288fca352e745521e5df1d193df5dd5 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 19:11:28 -0600 Subject: [PATCH 191/195] Fix crash in rail corridor generation, fix missing globals warnings, fix floating rail in rail corridors --- mods/ENTITIES/mcl_minecarts/functions.lua | 7 +++++-- mods/MAPGEN/tsm_railcorridors/gameconfig.lua | 2 +- mods/MAPGEN/tsm_railcorridors/init.lua | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 026f1ffc9..db981981f 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -197,7 +197,7 @@ local function get_rail_connections(pos, opt) local connections = 0 for i = 1,#CONNECTIONS do - dir = CONNECTIONS[i] + local dir = CONNECTIONS[i] local neighbor = vector.add(pos, dir) local node = force_get_node(neighbor) local nodedef = minetest.registered_nodes[node.name] @@ -347,6 +347,9 @@ local function update_rail_connections(pos, opt) end end + -- Recursion guard + if opt and opt.convert_neighbors == false then return end + -- Check if the open end of this rail runs into a corner or a tee and convert that node into a tee or a cross local neighbors = {} for i=1,#CONNECTIONS do @@ -356,7 +359,7 @@ local function update_rail_connections(pos, opt) local other_node = core.get_node(other_pos) local other_node_def = core.registered_nodes[other_node.name] local railtype = get_path(other_node_def, "_mcl_minecarts","railtype") - if (not opt or opt.convert_neighbors ~= false) and railtype == "corner" or railtype == "tee" then + if railtype == "corner" or railtype == "tee" then update_rail_connections(other_pos, {convert_neighbors = false}) end end diff --git a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua index 177ddef9d..084dacb34 100644 --- a/mods/MAPGEN/tsm_railcorridors/gameconfig.lua +++ b/mods/MAPGEN/tsm_railcorridors/gameconfig.lua @@ -80,7 +80,7 @@ end -- * entity_id - type of cart to create -- * pos: Position of cart -- * pr: pseudorandom -function tsm_railcorridors.create_cart_staticdata(entity_id, pos, pr) +function tsm_railcorridors.create_cart_staticdata(entity_id, pos, pr, pr_carts) local uuid = create_minecart(entity_id, pos, vector.new(1,0,0)) -- Fill the cart with loot diff --git a/mods/MAPGEN/tsm_railcorridors/init.lua b/mods/MAPGEN/tsm_railcorridors/init.lua index 8f1de762c..2d9043ae7 100644 --- a/mods/MAPGEN/tsm_railcorridors/init.lua +++ b/mods/MAPGEN/tsm_railcorridors/init.lua @@ -205,7 +205,7 @@ local function IsRailSurface(pos) local nodename = minetest.get_node(pos).name local nodename_above = minetest.get_node({x=pos.x,y=pos.y+2,z=pos.z}).name local nodedef = minetest.registered_nodes[nodename] - return nodename ~= "unknown" and nodename ~= "ignore" and nodedef and nodedef.walkable and (nodedef.node_box == nil or nodedef.node_box.type == "regular") and nodename_above ~= tsm_railcorridors.nodes.rail + return nodename ~= "unknown" and nodename ~= "ignore" and nodedef and nodedef.walkable and (nodedef.node_box == nil or nodedef.node_box.type == "regular") and nodename_above ~= tsm_railcorridors.nodes.rail and nodename ~= tsm_railcorridors.nodes.rail end -- Checks if the node is empty space which requires to be filled by a platform @@ -924,7 +924,7 @@ local function spawn_carts() -- Try to create cart staticdata local hook = tsm_railcorridors.create_cart_staticdata - if hook then cart_staticdata = hook(cart_id, cpos, pr) end + if hook then cart_staticdata = hook(cart_id, cpos, pr, pr_carts) end minetest.add_entity(cpos, cart_id, cart_staticdata) end From 1656262156cbe0e483bd81724737b3fd3e74c2e7 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Sun, 29 Dec 2024 20:21:02 -0600 Subject: [PATCH 192/195] Re-enable trains by default, add to settingtypes.txt --- mods/ENTITIES/mcl_minecarts/movement.lua | 2 +- settingtypes.txt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index d20820326..cf27bd315 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -3,7 +3,7 @@ local modpath = minetest.get_modpath(modname) local mod = mcl_minecarts local S = minetest.get_translator(modname) local submod = {} -local ENABLE_TRAINS = core.settings:get_bool("mcl_minecarts_experimental_trains",false) +local ENABLE_TRAINS = core.settings:get_bool("mcl_minecarts_enable_trains",true) -- Constants local mcl_debug,DEBUG = mcl_util.make_mcl_logger("mcl_logging_minecart_debug", "Minecart Debug") diff --git a/settingtypes.txt b/settingtypes.txt index 86c635582..ec829b783 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -314,6 +314,9 @@ enable_real_maps (Enable Real Maps) bool true # Hack 1: teleport golems home if they are very far from home mcl_mob_allow_nav_hacks (Mob navigation hacks) bool false +# Enable minecart trains +mcl_minecarts_enable_trains (Enable minecart trains) bool true + [Additional Features] # Enable Bookshelf inventories mcl_bookshelf_inventories (Enable bookshelf inventories) bool true From b5754d63a2a26edde5e4d53aed770adf43aa0e04 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 30 Dec 2024 18:21:39 -0600 Subject: [PATCH 193/195] Remove temporary description string, add assert to force description field to be present in rail definitions and fix descriptions for on variants of rail --- mods/ENTITIES/mcl_minecarts/rails.lua | 2 +- mods/ENTITIES/mcl_minecarts/rails/activator.lua | 1 + mods/ENTITIES/mcl_minecarts/rails/detector.lua | 1 + mods/ENTITIES/mcl_minecarts/rails/powered.lua | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_minecarts/rails.lua b/mods/ENTITIES/mcl_minecarts/rails.lua index 3163763f5..2356c7b5f 100644 --- a/mods/ENTITIES/mcl_minecarts/rails.lua +++ b/mods/ENTITIES/mcl_minecarts/rails.lua @@ -122,7 +122,6 @@ local BASE_DEF = { dig_by_water=0,destroy_by_lava_flow=0, transport=1 }, - description = S("New Rail"), -- Temporary name to make debugging easier _tt_help = S("Track for minecarts"), _doc_items_usagehelp = railuse, _doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."), @@ -186,6 +185,7 @@ table_merge(SLOPED_RAIL_DEF,{ function mod.register_rail(itemstring, ndef) assert(ndef.tiles) + assert(ndef.description) -- Extract out the craft recipe local craft = ndef.craft diff --git a/mods/ENTITIES/mcl_minecarts/rails/activator.lua b/mods/ENTITIES/mcl_minecarts/rails/activator.lua index 593613b35..605ba4a2f 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/activator.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/activator.lua @@ -51,6 +51,7 @@ mod.register_curves_rail("mcl_minecarts:activator_rail_v2_on", { "mcl_minecarts_rail_activator_t_junction_powered.png", "mcl_minecarts_rail_activator_crossing_powered.png" },{ + description = S("Activator Rail"), _doc_items_create_entry = false, groups = { not_in_creative_inventory = 1, diff --git a/mods/ENTITIES/mcl_minecarts/rails/detector.lua b/mods/ENTITIES/mcl_minecarts/rails/detector.lua index fde472f10..443cecf07 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/detector.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/detector.lua @@ -48,6 +48,7 @@ mod.register_curves_rail("mcl_minecarts:detector_rail_v2_on",{ "mcl_minecarts_rail_detector_t_junction_powered.png", "mcl_minecarts_rail_detector_crossing_powered.png" },{ + description = S("Detector Rail"), groups = { not_in_creative_inventory = 1, }, diff --git a/mods/ENTITIES/mcl_minecarts/rails/powered.lua b/mods/ENTITIES/mcl_minecarts/rails/powered.lua index 3adb1f0c9..e76aff2c7 100644 --- a/mods/ENTITIES/mcl_minecarts/rails/powered.lua +++ b/mods/ENTITIES/mcl_minecarts/rails/powered.lua @@ -46,6 +46,7 @@ mod.register_curves_rail("mcl_minecarts:golden_rail_v2_on",{ "mcl_minecarts_rail_golden_t_junction_powered.png", "mcl_minecarts_rail_golden_crossing_powered.png", },{ + description = S("Powered Rail"), _doc_items_create_entry = false, _rail_acceleration = function(pos, staticdata) if staticdata.velocity ~= 0 then From 7bf2e08baa4e7f25a4e898aadaa6dd3d49527ae6 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 30 Dec 2024 18:31:32 -0600 Subject: [PATCH 194/195] Fix cart direction when starting to move forward --- mods/ENTITIES/mcl_minecarts/movement.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/movement.lua b/mods/ENTITIES/mcl_minecarts/movement.lua index cf27bd315..ce449a89b 100644 --- a/mods/ENTITIES/mcl_minecarts/movement.lua +++ b/mods/ENTITIES/mcl_minecarts/movement.lua @@ -259,7 +259,7 @@ local function direction_away_from_players(staticdata) end local look_directions = { - mod.north, + [0] = mod.north, mod.west, mod.south, mod.east, @@ -283,7 +283,7 @@ local function calculate_acceleration(staticdata) local time_active = minetest.get_gametime() - 0.25 if (ctrl.forward or 0) > time_active then - if staticdata.velocity == 0 then + if staticdata.velocity <= 0.05 then local look_dir = look_directions[ctrl.look or 0] or mod.north local dot = vector.dot(staticdata.dir, look_dir) if dot < 0 then From 290e2e9753b5fbf3bae432324b54797978690dd7 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 30 Dec 2024 18:42:29 -0600 Subject: [PATCH 195/195] Fix cart placement so that furnace minecarts will always try to start moving in the direction the player was facing when the cart was placed --- mods/ENTITIES/mcl_minecarts/carts.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index afd23924b..ecf9eca7e 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -389,8 +389,12 @@ function mod.place_minecart(itemstack, pointed_thing, placer) return end + local look_4dir = math.round(placer:get_look_horizontal() * TWO_OVER_PI) % 4 + local look_dir = core.fourdir_to_dir(look_4dir) + look_dir.x = -look_dir.x + local spawn_pos = pointed_thing.above - local cart_dir = vector.new(1,0,0) + local cart_dir = look_dir local railpos, node if mcl_minecarts.is_rail(pointed_thing.under) then @@ -403,8 +407,8 @@ function mod.place_minecart(itemstack, pointed_thing, placer) node = minetest.get_node(railpos) -- Try two orientations, and select the second if the first is at an angle - cart_dir1 = mcl_minecarts.get_rail_direction(railpos, vector.new( 1,0,0)) - cart_dir2 = mcl_minecarts.get_rail_direction(railpos, vector.new(-1,0,0)) + cart_dir1 = mcl_minecarts.get_rail_direction(railpos, look_dir) + cart_dir2 = mcl_minecarts.get_rail_direction(railpos, -look_dir) if vector.length(cart_dir1) <= 1 then cart_dir = cart_dir1 else