diff --git a/minecart/README.md b/minecart/README.md index 444fe16..4605cd4 100644 --- a/minecart/README.md +++ b/minecart/README.md @@ -48,6 +48,8 @@ The mod features are: - Minecarts run through unloaded areas (only the stations/hopper have to be loaded) - Extra Minecart privs for rail workers - Ingame documentation (German and English), based on the mod "doc" +- API to register carts from other mods +- chat command '/mycart ' to output cart state and location Technical Background @@ -67,18 +69,21 @@ Introduction 2. Place a Railway Buffer at both endpoints. (buffers are always needed, they store the route and timing information) 3. Give both Railway Buffers unique station names, like Oxford and Cambridge -4. Drive from buffer to buffer in both directions using a Minecart(!) to record the +4. Place a Minecart at a buffer and give it a cart number (1..999) +5. Drive from buffer to buffer in both directions using the Minecart(!) to record the routes (use 'right-left' keys to control the Minecart) -5. Punch the buffers to check the connection data (e.g. "Oxford: connected to Cambridge") -6. Optional: Configure the Minecart stop time in one or both buffers. The Minecart +6. Punch the buffers to check the connection data (e.g. "Oxford: connected to Cambridge") +7. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time -7. Optional: Protect your rail network with the Protection Landmarks (one Landmark +8. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters) -8. Place a Minecart in front of the buffer and check whether it starts after the +9. Place a Minecart in front of the buffer and check whether it starts after the configured time -9. Drop items into the Minecart and punch the cart to start it, or "sneak+click" the - Minecart to get the items back -10. Dig the empty cart with a second "sneak+click" (as usual) +10. Check the cart state via the chat command: /mycart + '' is the cart number +11. Drop items into the Minecart and punch the cart to start it, or "sneak+click" the + Minecart to get the items back +12. Dig the empty cart with a second "sneak+click" (as usual) Hopper @@ -107,4 +112,6 @@ History 2020-02-24 v1.02 Hopper improved 2020-03-05 v1.03 Hopper again improved 2020-03-28 v1.04 cart unloading bugfix +2020-05-14 v1.05 API changed to be able to register carts +2020-06-14 v1.06 API changed and chat command added diff --git a/minecart/cart_api.lua b/minecart/cart_api.lua deleted file mode 100644 index 7b384bd..0000000 --- a/minecart/cart_api.lua +++ /dev/null @@ -1,450 +0,0 @@ ---[[ - - Minecart - ======== - - Copyright (C) 2019-2020 Joachim Stolberg - - MIT - See license.txt for more information - - Cart API for external cart definitions on a node based model - -]]-- - --- for lazy programmers -local M = minetest.get_meta -local S = minecart.S -local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end -local S2P = minetest.string_to_pos - --- register cart here, because entity is already registered -minecart.register_cart_names("minecart:cart", "minecart:cart") - -function minecart.register_cart_entity(entity_name, node_name, entity_def) - entity_def.velocity = {x=0, y=0, z=0} -- only used on punch - entity_def.old_dir = {x=1, y=0, z=0} -- random value to start the cart on punch - entity_def.old_pos = nil - entity_def.old_switch = 0 - entity_def.node_name = node_name - minetest.register_entity(entity_name, entity_def) - -- register node for punching - minecart.register_cart_names(node_name, entity_name) -end - -local function switch_to_node(pos, node_name, owner, param2, cargo) - local node = minetest.get_node(pos) - local rail = node.name - local ndef = minetest.registered_nodes[node_name] - if ndef then - node.name = node_name - node.param2 = param2 - minetest.add_node(pos, node) - M(pos):set_string("removed_rail", rail) - M(pos):set_string("owner", owner) - if ndef.after_place_node then - ndef.after_place_node(pos) - end - if cargo and ndef.set_cargo then - ndef.set_cargo(pos, cargo) - end - end - -end - -function minecart.node_on_place(itemstack, placer, pointed_thing, node_name) - local under = pointed_thing.under - local node = minetest.get_node(under) - local udef = minetest.registered_nodes[node.name] - if udef and udef.on_rightclick and - not (placer and placer:is_player() and - placer:get_player_control().sneak) then - return udef.on_rightclick(under, node, placer, itemstack, - pointed_thing) or itemstack - end - - if not pointed_thing.type == "node" then - return - end - local owner = placer:get_player_name() - local param2 = minetest.dir_to_facedir(placer:get_look_dir()) - if carts:is_rail(pointed_thing.under) then - switch_to_node(pointed_thing.under, node_name, owner, param2) - elseif carts:is_rail(pointed_thing.above) then - switch_to_node(pointed_thing.above, node_name, owner, param2) - else - return - end - - minetest.sound_play({name = "default_place_node_metal", gain = 0.5}, - {pos = pointed_thing.above}) - - if not (creative and creative.is_enabled_for - and creative.is_enabled_for(placer:get_player_name())) then - itemstack:take_item() - end - return itemstack -end - -function minecart.node_on_punch(pos, node, puncher, pointed_thing, entity_name, dir) - local ndef = minetest.registered_nodes[node.name] - local cargo = {} - -- Player digs cart by sneak-punch - if puncher and puncher:get_player_control().sneak then - -- Pick up cart - if ndef.can_dig and ndef.can_dig(pos, puncher) then - local inv = puncher:get_inventory() - if not (creative and creative.is_enabled_for - and creative.is_enabled_for(puncher:get_player_name())) - or not inv:contains_item("main", node.name) then - local leftover = inv:add_item("main", node.name) - -- If no room in inventory add a replacement cart to the world - if not leftover:is_empty() then - minetest.add_item(pos, leftover) - end - end - node.name = M(pos):get_string("removed_rail") - if node.name == "" then - node.name = "carts:rail" - end - minetest.remove_node(pos) - minetest.add_node(pos, node) - end - return - end - -- start cart - node.name = M(pos):get_string("removed_rail") - if node.name ~= "" then - if ndef.get_cargo then - cargo = ndef.get_cargo(pos) - end - minetest.add_node(pos, node) - local obj = minetest.add_entity(pos, entity_name) - local owner = puncher and puncher:get_player_name() - minecart.add_cart_to_monitoring(obj, owner, cargo) - obj:punch(puncher or obj, 1, { - full_punch_interval = 1.0, - damage_groups = {fleshy = 1}, - }, dir) - end -end - -function minecart:on_activate(staticdata, dtime_s) - self.object:set_armor_groups({immortal=1}) -end - - -function minecart:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) - --print("on_punch", direction) - local pos = self.object:get_pos() - local vel = self.object:get_velocity() - local stopped = vector.equals(vel, {x=0, y=0, z=0}) - - -- running carts can't be punched - if not stopped then - return - end - - if not self.railtype then - local node = minetest.get_node(pos).name - self.railtype = minetest.get_item_group(node, "connect_to_raillike") - end - - -- Punched by non-authorized player - if puncher and self.owner and self.owner ~= puncher:get_player_name() - and not minetest.check_player_privs(puncher:get_player_name(), "minecart") then - return - end - - -- Punched by non-player - if not puncher or not puncher:is_player() then - local cart_dir = carts:get_rail_direction(pos, direction, nil, nil, self.railtype) - if vector.equals(cart_dir, {x=0, y=0, z=0}) then - return - end - self.velocity = vector.multiply(cart_dir, 2) - self.punched = true - return - end - - -- Player digs cart by sneak-punch - if puncher:get_player_control().sneak then - if self.sound_handle then - minetest.sound_stop(self.sound_handle) - end - - -- Pick up cart - local node_name = self.node_name or "minecart:cart" - local inv = puncher:get_inventory() - if not (creative and creative.is_enabled_for - and creative.is_enabled_for(puncher:get_player_name())) - or not inv:contains_item("main", node_name) then - local leftover = inv:add_item("main", node_name) - -- If no room in inventory add a replacement cart to the world - if not leftover:is_empty() then - minetest.add_item(self.object:get_pos(), leftover) - end - end - minecart.on_dig(self) - self.object:remove() - return - end - - local punch_dir = carts:velocity_to_dir(puncher:get_look_dir()) - punch_dir.y = 0 - local cart_dir = carts:get_rail_direction(pos, punch_dir, nil, nil, self.railtype) - if vector.equals(cart_dir, {x=0, y=0, z=0}) then - return - end - - self.velocity = vector.multiply(cart_dir, 2) - self.old_dir = cart_dir - self.punched = true -end - --- sound refresh interval = 1.0sec -local function rail_sound(self, dtime) - if not self.sound_ttl then - self.sound_ttl = 1.0 - return - elseif self.sound_ttl > 0 then - self.sound_ttl = self.sound_ttl - dtime - return - end - self.sound_ttl = 1.0 - if self.sound_handle then - local handle = self.sound_handle - self.sound_handle = nil - minetest.after(0.2, minetest.sound_stop, handle) - end - local vel = self.object:get_velocity() or {x=0, y=0, z=0} - local speed = vector.length(vel) - if speed > 0 then - self.sound_handle = minetest.sound_play( - "carts_cart_moving", { - object = self.object, - gain = (speed / carts.speed_max) / 2, - loop = true, - }) - end -end - -local function get_railparams(pos) - local node = minetest.get_node(pos) - return carts.railparams[node.name] or {} -end - -local function rail_on_step(self, dtime) - local vel = self.object:get_velocity() - local pos = self.object:get_pos() - - if self.punched then - minecart.start_run(self, pos, vel, self.driver) - 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}) then - if minecart.get_route_key(pos) then - local cargo = minecart.stopped(self, pos) - local param2 = minetest.dir_to_facedir(self.old_dir) - switch_to_node(vector.round(pos), self.node_name, self.owner, param2, cargo) - minecart.on_dig(self) - self.object:remove() - end - return - end - - -- cart position correction on slopes - local rot = self.object:get_rotation() - if rot.x ~= 0 then - pos.y = pos.y - 0.5 - end - - local cart_dir = carts:velocity_to_dir(vel) - local same_dir = vector.equals(cart_dir, self.old_dir) - local update = {} - - if self.old_pos and not self.punched and same_dir then - local flo_pos = vector.round(pos) - local flo_old = vector.round(self.old_pos) - if vector.equals(flo_pos, flo_old) then - -- Do not check one node multiple times - return - end - end - - local ctrl, player - - - local stop_wiggle = false - if self.old_pos and same_dir then - -- Detection for "skipping" nodes (perhaps use average dtime?) - -- It's sophisticated enough to take the acceleration in account - local acc = self.object:get_acceleration() - local distance = dtime * (vector.length(vel) + 0.5 * dtime * vector.length(acc)) - - local new_pos, new_dir = carts:pathfinder( - pos, self.old_pos, self.old_dir, distance, ctrl, - self.old_switch, self.railtype - ) - - if new_pos then - -- No rail found: set to the expected position - pos = new_pos - update.pos = true - cart_dir = new_dir - end - elseif self.old_pos and self.old_dir.y ~= 1 and not self.punched then - -- Stop wiggle - stop_wiggle = true - end - - local railparams - - -- dir: New moving direction of the cart - -- switch_keys: Currently pressed L/R key, used to ignore the key on the next rail node - local dir, switch_keys = carts:get_rail_direction( - pos, cart_dir, ctrl, self.old_switch, self.railtype - ) - ------------------------------- changed - if switch_keys then - minecart.set_junction(self, pos, dir, switch_keys) - else - dir, switch_keys = minecart.get_junction(self, pos, dir) - end - ------------------------------- changed - local dir_changed = not vector.equals(dir, self.old_dir) - - local new_acc = {x=0, y=0, z=0} - if stop_wiggle or vector.equals(dir, {x=0, y=0, z=0}) then - vel = {x = 0, y = 0, z = 0} - local pos_r = vector.round(pos) - if not carts:is_rail(pos_r, self.railtype) - and self.old_pos then - pos = self.old_pos - elseif not stop_wiggle then - pos = pos_r - else - pos.y = math.floor(pos.y + 0.5) - end - update.pos = true - update.vel = true - else - -- Direction change detected - if dir_changed then - vel = vector.multiply(dir, math.abs(vel.x + vel.z)) - update.vel = true - if dir.y ~= self.old_dir.y then - pos = vector.round(pos) - update.pos = true - end - end - -- Center on the rail - if dir.z ~= 0 and math.floor(pos.x + 0.5) ~= pos.x then - pos.x = math.floor(pos.x + 0.5) - update.pos = true - end - if dir.x ~= 0 and math.floor(pos.z + 0.5) ~= pos.z then - pos.z = math.floor(pos.z + 0.5) - update.pos = true - end - - -- Slow down or speed up.. - local acc = dir.y * -4.0 - - -- Get rail for corrected position - railparams = get_railparams(pos) - - -- no need to check for railparams == nil since we always make it exist. - local speed_mod = railparams.acceleration - if speed_mod and speed_mod ~= 0 then - -- Try to make it similar to the original carts mod - acc = acc + speed_mod - else - -- Handbrake or coast - if ctrl and ctrl.down then - acc = acc - 3 - else - acc = acc - 0.4 - end - end - - new_acc = vector.multiply(dir, acc) - end - - -- Limits - local max_vel = carts.speed_max - for _, v in pairs({"x","y","z"}) do - if math.abs(vel[v]) > max_vel then - vel[v] = carts:get_sign(vel[v]) * max_vel - new_acc[v] = 0 - update.vel = true - end - end - - self.object:set_acceleration(new_acc) - self.old_pos = vector.round(pos) - if not vector.equals(dir, {x=0, y=0, z=0}) and not stop_wiggle then - self.old_dir = vector.new(dir) - end - self.old_switch = switch_keys - - if self.punched then - self.punched = false - update.vel = true - end - - railparams = railparams or get_railparams(pos) - - if not (update.vel or update.pos) then - return - end - - local yaw = 0 - if self.old_dir.x < 0 then - yaw = math.pi/2*3 - elseif self.old_dir.x > 0 then - yaw = math.pi/2 - elseif self.old_dir.z < 0 then - yaw = math.pi - end - --self.object:set_yaw(yaw * math.pi) - - local pitch = 0 - if self.old_dir.z ~= 0 then - if dir.y == -1 then - pitch = -math.pi/4 - elseif dir.y == 1 then - pitch = math.pi/4 - end - else - if dir.y == -1 then - pitch = math.pi/4 - elseif dir.y == 1 then - pitch = -math.pi/4 - end - end - self.object:set_rotation({x = pitch, y = yaw, z = 0}) - - -- cart position correction on slopes - if pitch ~= 0 then - pos.y = pos.y + 0.5 - update.pos = true - end - - if update.vel then - self.object:set_velocity(vel) - end - if update.pos then - if dir_changed then - self.object:set_pos(pos) - else - self.object:move_to(pos) - end - end -end - -function minecart:on_step(dtime) - rail_on_step(self, dtime) - rail_sound(self, dtime) -end diff --git a/minecart/cart_entity.lua b/minecart/cart_entity.lua deleted file mode 100644 index 8d03993..0000000 --- a/minecart/cart_entity.lua +++ /dev/null @@ -1,468 +0,0 @@ -local S = minecart.S - -local cart_entity = { - initial_properties = { - physical = false, -- otherwise going uphill breaks - collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, - visual = "mesh", - mesh = "carts_cart.b3d", - visual_size = {x=1, y=1}, - textures = {"carts_cart.png^minecart_cart.png"}, - static_save = false, - }, - ------------------------------------ changed - owner = nil, - ------------------------------------ changed - driver = nil, - punched = false, -- used to re-send velocity and position - velocity = {x=0, y=0, z=0}, -- only used on punch - old_dir = {x=1, y=0, z=0}, -- random value to start the cart on punch - old_pos = nil, - old_switch = 0, - railtype = nil, - attached_items = {} -} - -function cart_entity:on_rightclick(clicker) - 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 - self.driver = nil - carts:manage_attachment(clicker, nil) - elseif not self.driver then - self.driver = player_name - carts:manage_attachment(clicker, self.object) - - -- player_api does not update the animation - -- when the player is attached, reset to default animation - player_api.set_animation(clicker, "stand") - end -end - -function cart_entity:on_activate(staticdata, dtime_s) - self.object:set_armor_groups({immortal=1}) -end - --- 0.5.x and later: When the driver leaves -function cart_entity:on_detach_child(child) - if child and child:get_player_name() == self.driver then - self.driver = nil - end -end - -local function add_cargo_to_player_inv(self, pos, puncher) - local added = false - local inv = puncher:get_inventory() - for _, obj in pairs(minetest.get_objects_inside_radius(pos, 1)) do - local entity = obj:get_luaentity() - if not obj:is_player() and entity and entity.name == "__builtin:item" then - obj:remove() - local item = ItemStack(entity.itemstring) - local leftover = inv:add_item("main", item) - if leftover:get_count() > 0 then - minetest.add_item(pos, leftover) - end - added = true - end - end - return added -end - -function cart_entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) - local pos = self.object:get_pos() - local vel = self.object:get_velocity() - if not self.railtype or vector.equals(vel, {x=0, y=0, z=0}) then - local node = minetest.get_node(pos).name - self.railtype = minetest.get_item_group(node, "connect_to_raillike") - end - -- Punched by non-player - if not puncher or not puncher:is_player() then - local cart_dir = carts:get_rail_direction(pos, direction, nil, nil, self.railtype) - if vector.equals(cart_dir, {x=0, y=0, z=0}) then - return - end - self.velocity = vector.multiply(cart_dir, 2) - self.punched = true - return - end - ------------------------------------ changed - -- Punched by non-authorized player - if puncher and self.owner and self.owner ~= puncher:get_player_name() - and not minetest.check_player_privs(puncher:get_player_name(), "minecart") then - return - end - ------------------------------------ changed - -- Player digs cart by sneak-punch - if puncher:get_player_control().sneak then - if self.sound_handle then - minetest.sound_stop(self.sound_handle) - end - -- Detach driver and items - if self.driver then - if self.old_pos then - self.object:set_pos(self.old_pos) - end - local player = minetest.get_player_by_name(self.driver) - carts:manage_attachment(player, nil) - end - ------------------------------------ changed - if add_cargo_to_player_inv(self, pos, puncher) then - return - end - ------------------------------------ changed - -- Pick up cart - local inv = puncher:get_inventory() - if not (creative and creative.is_enabled_for - and creative.is_enabled_for(puncher:get_player_name())) - or not inv:contains_item("main", "minecart:cart") then - local leftover = inv:add_item("main", "minecart:cart") - -- If no room in inventory add a replacement cart to the world - if not leftover:is_empty() then - minetest.add_item(self.object:get_pos(), leftover) - end - end - ------------------------------------ changed - minecart.on_dig(self) - ------------------------------------ changed - self.object:remove() - return - end - ------------------------------------ changed - minecart.start_recording(self, pos, vel, puncher) - ------------------------------------ changed - -- Player punches cart to alter velocity - if puncher:get_player_name() == self.driver then - if math.abs(vel.x + vel.z) > carts.punch_speed_max then - return - end - end - - local punch_dir = carts:velocity_to_dir(puncher:get_look_dir()) - punch_dir.y = 0 - local cart_dir = carts:get_rail_direction(pos, punch_dir, nil, nil, self.railtype) - if vector.equals(cart_dir, {x=0, y=0, z=0}) then - return - end - - local punch_interval = 1 - if tool_capabilities and tool_capabilities.full_punch_interval then - punch_interval = tool_capabilities.full_punch_interval - end - time_from_last_punch = math.min(time_from_last_punch or punch_interval, punch_interval) - local f = 2 * (time_from_last_punch / punch_interval) - - ------------------------------------ changed - if vector.equals(vel, {x=0, y=0, z=0}) then - self.velocity = vector.multiply(cart_dir, f) - else - self.velocity = {x=0, y=0, z=0} - end - ------------------------------------ changed - self.old_dir = cart_dir - self.punched = true -end - -local function rail_on_step_event(handler, obj, dtime) - if handler then - handler(obj, dtime) - end -end - --- sound refresh interval = 1.0sec -local function rail_sound(self, dtime) - if not self.sound_ttl then - self.sound_ttl = 1.0 - return - elseif self.sound_ttl > 0 then - self.sound_ttl = self.sound_ttl - dtime - return - end - self.sound_ttl = 1.0 - if self.sound_handle then - local handle = self.sound_handle - self.sound_handle = nil - minetest.after(0.2, minetest.sound_stop, handle) - end - local vel = self.object:get_velocity() - local speed = vector.length(vel) - if speed > 0 then - self.sound_handle = minetest.sound_play( - "carts_cart_moving", { - object = self.object, - gain = (speed / carts.speed_max) / 2, - loop = true, - }) - end -end - -local function get_railparams(pos) - local node = minetest.get_node(pos) - return carts.railparams[node.name] or {} -end - -local v3_len = vector.length -local function rail_on_step(self, dtime) - local vel = self.object:get_velocity() - ------------------------------------ changed - local pos = self.object:get_pos() - minecart.store_next_waypoint(self, pos, vel) - ------------------------------------ changed - if self.punched then - ------------------------------- changed - minecart.start_run(self, pos, vel, self.driver) - ------------------------------- changed - 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}) then - ------------------------------- changed - minecart.stopped(self, pos) - ------------------------------- changed - return - end - - --local pos = self.object:get_pos() - local cart_dir = carts:velocity_to_dir(vel) - local same_dir = vector.equals(cart_dir, self.old_dir) - local update = {} - - if self.old_pos and not self.punched and same_dir then - local flo_pos = vector.round(pos) - local flo_old = vector.round(self.old_pos) - if vector.equals(flo_pos, flo_old) then - -- Do not check one node multiple times - return - end - end - - local ctrl, player - - -- Get player controls - if self.driver then - player = minetest.get_player_by_name(self.driver) - if player then - ctrl = player:get_player_control() - end - end - - local stop_wiggle = false - if self.old_pos and same_dir then - -- Detection for "skipping" nodes (perhaps use average dtime?) - -- It's sophisticated enough to take the acceleration in account - local acc = self.object:get_acceleration() - local distance = dtime * (v3_len(vel) + 0.5 * dtime * v3_len(acc)) - - local new_pos, new_dir = carts:pathfinder( - pos, self.old_pos, self.old_dir, distance, ctrl, - self.old_switch, self.railtype - ) - - if new_pos then - -- No rail found: set to the expected position - pos = new_pos - update.pos = true - cart_dir = new_dir - end - elseif self.old_pos and self.old_dir.y ~= 1 and not self.punched then - -- Stop wiggle - stop_wiggle = true - end - - local railparams - - -- dir: New moving direction of the cart - -- switch_keys: Currently pressed L/R key, used to ignore the key on the next rail node - local dir, switch_keys = carts:get_rail_direction( - pos, cart_dir, ctrl, self.old_switch, self.railtype - ) - ------------------------------- changed - if switch_keys then - minecart.set_junction(self, pos, dir, switch_keys) - else - dir, switch_keys = minecart.get_junction(self, pos, dir) - end - ------------------------------- changed - local dir_changed = not vector.equals(dir, self.old_dir) - - local new_acc = {x=0, y=0, z=0} - if stop_wiggle or vector.equals(dir, {x=0, y=0, z=0}) then - vel = {x = 0, y = 0, z = 0} - local pos_r = vector.round(pos) - if not carts:is_rail(pos_r, self.railtype) - and self.old_pos then - pos = self.old_pos - elseif not stop_wiggle then - pos = pos_r - else - pos.y = math.floor(pos.y + 0.5) - end - update.pos = true - update.vel = true - else - -- Direction change detected - if dir_changed then - vel = vector.multiply(dir, math.abs(vel.x + vel.z)) - update.vel = true - if dir.y ~= self.old_dir.y then - pos = vector.round(pos) - update.pos = true - end - end - -- Center on the rail - if dir.z ~= 0 and math.floor(pos.x + 0.5) ~= pos.x then - pos.x = math.floor(pos.x + 0.5) - update.pos = true - end - if dir.x ~= 0 and math.floor(pos.z + 0.5) ~= pos.z then - pos.z = math.floor(pos.z + 0.5) - update.pos = true - end - - -- Slow down or speed up.. - local acc = dir.y * -4.0 - - -- Get rail for corrected position - railparams = get_railparams(pos) - - -- no need to check for railparams == nil since we always make it exist. - local speed_mod = railparams.acceleration - if speed_mod and speed_mod ~= 0 then - -- Try to make it similar to the original carts mod - acc = acc + speed_mod - else - -- Handbrake or coast - if ctrl and ctrl.down then - acc = acc - 3 - else - acc = acc - 0.4 - end - end - - new_acc = vector.multiply(dir, acc) - end - - -- Limits - local max_vel = carts.speed_max - for _, v in pairs({"x","y","z"}) do - if math.abs(vel[v]) > max_vel then - vel[v] = carts:get_sign(vel[v]) * max_vel - new_acc[v] = 0 - update.vel = true - end - end - - self.object:set_acceleration(new_acc) - self.old_pos = vector.round(pos) - if not vector.equals(dir, {x=0, y=0, z=0}) and not stop_wiggle then - self.old_dir = vector.new(dir) - end - self.old_switch = switch_keys - - if self.punched then - -- Collect dropped items - ------------------------------- changed - minecart.attach_cargo(self, pos) - ------------------------------- changed - self.punched = false - update.vel = true - end - - railparams = railparams or get_railparams(pos) - - if not (update.vel or update.pos) then - rail_on_step_event(railparams.on_step, self, dtime) - return - end - - local yaw = 0 - if self.old_dir.x < 0 then - yaw = 0.5 - elseif self.old_dir.x > 0 then - yaw = 1.5 - elseif self.old_dir.z < 0 then - yaw = 1 - end - self.object:set_yaw(yaw * math.pi) - - 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) - - if update.vel then - self.object:set_velocity(vel) - end - if update.pos then - if dir_changed then - self.object:set_pos(pos) - else - self.object:move_to(pos) - end - end - - -- call event handler - rail_on_step_event(railparams.on_step, self, dtime) -end - -function cart_entity:on_step(dtime) - rail_on_step(self, dtime) - rail_sound(self, dtime) -end - -minetest.register_entity("minecart:cart", cart_entity) - -minetest.register_craftitem("minecart:cart", { - description = S("Minecart (Sneak+Click to pick up)"), - inventory_image = minetest.inventorycube("carts_cart_top.png", "carts_cart_side.png^minecart_logo.png", "carts_cart_side.png^minecart_logo.png"), - wield_image = "carts_cart_side.png", - on_place = function(itemstack, placer, pointed_thing) - local under = pointed_thing.under - local node = minetest.get_node(under) - local udef = minetest.registered_nodes[node.name] - if udef and udef.on_rightclick and - not (placer and placer:is_player() and - placer:get_player_control().sneak) then - return udef.on_rightclick(under, node, placer, itemstack, - pointed_thing) or itemstack - end - - if not pointed_thing.type == "node" then - return - end - if carts:is_rail(pointed_thing.under) then - ------------------------------- changed - local cart = minetest.add_entity(pointed_thing.under, "minecart:cart") - minecart.add_cart_to_monitoring(cart, placer:get_player_name()) - ------------------------------- changed - elseif carts:is_rail(pointed_thing.above) then - ------------------------------- changed - local cart = minetest.add_entity(pointed_thing.above, "minecart:cart") - minecart.add_cart_to_monitoring(cart, placer:get_player_name()) - ------------------------------- changed - else - return - end - - minetest.sound_play({name = "default_place_node_metal", gain = 0.5}, - {pos = pointed_thing.above}) - - if not (creative and creative.is_enabled_for - and creative.is_enabled_for(placer:get_player_name())) then - itemstack:take_item() - end - return itemstack - end, -}) - -minetest.register_craft({ - output = "minecart:cart", - recipe = { - {"default:steel_ingot", "default:cobble", "default:steel_ingot"}, - {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, - }, -}) diff --git a/minecart/cart_lib1.lua b/minecart/cart_lib1.lua new file mode 100644 index 0000000..aa4ea0e --- /dev/null +++ b/minecart/cart_lib1.lua @@ -0,0 +1,416 @@ +--[[ + + Minecart + ======== + + Copyright (C) 2019-2020 Joachim Stolberg + + MIT + See license.txt for more information + + Cart library functions (level 1) + +]]-- + +-- Notes: +-- 1) Only the owner can punch der cart +-- 2) Only the owner can start the recording +-- 3) But any player can act as cargo, cart punched by owner or buffer + + +-- for lazy programmers +local M = minetest.get_meta +local S = minecart.S +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local S2P = minetest.string_to_pos +local MP = minetest.get_modpath("minecart") + +local api = {} + +function api:init(is_node_cart) + local lib + + if is_node_cart then + lib = dofile(MP.."/cart_lib2n.lua") + else + lib = dofile(MP.."/cart_lib2e.lua") + end + + -- add lib to local api + for k,v in pairs(lib) do + api[k] = v + end +end + +-- Player get on / off +function api:on_rightclick(clicker) + 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 + self.driver = nil + carts:manage_attachment(clicker, nil) + elseif not self.driver then + self.driver = player_name + carts:manage_attachment(clicker, self.object) + + -- player_api does not update the animation + -- when the player is attached, reset to default animation + player_api.set_animation(clicker, "stand") + end +end + +function api:on_activate(staticdata, dtime_s) + self.object:set_armor_groups({immortal=1}) +end + +function api:on_detach_child(child) + if child and child:get_player_name() == self.driver then + self.driver = nil + end +end + +function api:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) + local pos = self.object:get_pos() + local vel = self.object:get_velocity() + local stopped = vector.equals(vel, {x=0, y=0, z=0}) + local is_minecart = self.node_name == nil + local node_name = self.node_name or "minecart:cart" + local puncher_name = puncher and puncher:is_player() and puncher:get_player_name() + local puncher_is_owner = puncher_name and (not self.owner or self.owner == "" or + puncher_name == self.owner or + minetest.check_player_privs(puncher_name, "minecart")) + local puncher_is_driver = self.driver and self.driver == puncher_name + local sneak_punch = puncher_name and puncher:get_player_control().sneak + local no_cargo = next(self.cargo or {}) == nil + + -- driver wants to leave/remove the empty Minecart by sneak-punch + if is_minecart and sneak_punch and puncher_is_driver and no_cargo then + if puncher_is_owner then + api.remove_cart(self, pos, puncher) + end + carts:manage_attachment(puncher, nil) + return + end + + -- running carts can't be punched or removed from external + if not stopped then + return + end + + -- Punched by non-authorized player + if puncher_name and not puncher_is_owner then + minetest.chat_send_player(puncher_name, S("[minecart] Cart is protected by ")..(self.owner or "")) + return + end + + if not self.railtype then + local node = minetest.get_node(pos).name + self.railtype = minetest.get_item_group(node, "connect_to_raillike") + end + + -- Punched by non-player + if not puncher_name then + local cart_dir = carts:get_rail_direction(pos, direction, nil, nil, self.railtype) + if vector.equals(cart_dir, {x=0, y=0, z=0}) then + return + end + self.velocity = vector.multiply(cart_dir, 2) + self.punched = true + api.load_cargo(self, pos) + minecart.start_cart(pos, self.myID) + return + end + + -- Sneak-punched by owner + if sneak_punch then + -- Unload the cargo + if api.add_cargo_to_player_inv(self, pos, puncher) then + return + end + -- detach driver + if self.driver then + carts:manage_attachment(puncher_name, nil) + end + -- Pick up cart + api.remove_cart(self, pos, puncher) + return + end + + -- Cart with driver punched to start recording + if puncher_is_driver then + minecart.start_recording(self, pos, vel, puncher) + else + minecart.start_cart(pos, self.myID) + end + + api.load_cargo(self, pos) + + -- Normal punch by owner to start the cart + local punch_dir = carts:velocity_to_dir(puncher:get_look_dir()) + punch_dir.y = 0 + local cart_dir = carts:get_rail_direction(pos, punch_dir, nil, nil, self.railtype) + if vector.equals(cart_dir, {x=0, y=0, z=0}) then + return + end + + self.velocity = vector.multiply(cart_dir, 2) + self.old_dir = cart_dir + self.punched = true +end + +local function rail_on_step_event(handler, obj, dtime) + if handler then + handler(obj, dtime) + end +end + +-- sound refresh interval = 1.0sec +local function rail_sound(self, dtime) + if not self.sound_ttl then + self.sound_ttl = 1.0 + return + elseif self.sound_ttl > 0 then + self.sound_ttl = self.sound_ttl - dtime + return + end + self.sound_ttl = 1.0 + if self.sound_handle then + local handle = self.sound_handle + self.sound_handle = nil + minetest.after(0.2, minetest.sound_stop, handle) + end + if not self.stopped then + local vel = self.object:get_velocity() or {x=0, y=0, z=0} + local speed = vector.length(vel) + self.sound_handle = minetest.sound_play( + "carts_cart_moving", { + object = self.object, + gain = (speed / carts.speed_max) / 2, + loop = true, + }) + end +end + +local function get_railparams(pos) + local node = minetest.get_node(pos) + return carts.railparams[node.name] or {} +end + +local v3_len = vector.length +local function rail_on_step(self, dtime) + local vel = self.object:get_velocity() + local pos = self.object:get_pos() + local rot = self.object:get_rotation() + local stopped = minecart.stopped(vel) and rot.x == 0 + local is_minecart = self.node_name == nil + local recording = is_minecart and self.driver == self.owner + + -- cart position correction on slopes + if rot.x ~= 0 then + pos.y = pos.y - 0.5 + end + + if self.punched then + vel = vector.add(vel, self.velocity) + self.object:set_velocity(vel) + self.old_dir.y = 0 + self.stopped = false + elseif stopped and not self.stopped then + local param2 = minetest.dir_to_facedir(self.old_dir) + api.stop_cart(pos, self, self.node_name or "minecart:cart", param2) + if recording then + minecart.stop_recording(self, pos, vel, self.driver) + end + api.unload_cargo(self, pos) + self.stopped = true + self.object:set_velocity({x=0, y=0, z=0}) + self.object:set_acceleration({x=0, y=0, z=0}) + return + elseif stopped then + return + end + + if recording then + minecart.store_next_waypoint(self, pos, vel) + end + + local cart_dir = carts:velocity_to_dir(vel) + local same_dir = vector.equals(cart_dir, self.old_dir) + local update = {} + + if self.old_pos and not self.punched and same_dir then + local flo_pos = vector.round(pos) + local flo_old = vector.round(self.old_pos) + if vector.equals(flo_pos, flo_old) then + -- Do not check one node multiple times + return + end + end + + local ctrl, player + + -- Get player controls + if recording then + player = minetest.get_player_by_name(self.driver) + if player then + ctrl = player:get_player_control() + end + end + + local railparams + + -- dir: New moving direction of the cart + -- switch_keys: Currently pressed L/R key, used to ignore the key on the next rail node + local dir, switch_keys = carts:get_rail_direction( + pos, cart_dir, ctrl, self.old_switch, self.railtype + ) + + -- handle junctions + if switch_keys then -- recording + minecart.set_junction(self, pos, dir, switch_keys) + else -- normal run + dir, switch_keys = minecart.get_junction(self, pos, dir) + end + + local dir_changed = not vector.equals(dir, self.old_dir) + + local new_acc = {x=0, y=0, z=0} + if vector.equals(dir, {x=0, y=0, z=0}) then + vel = {x = 0, y = 0, z = 0} + local pos_r = vector.round(pos) + if not carts:is_rail(pos_r, self.railtype) + and self.old_pos then + pos = self.old_pos + else + pos = pos_r + end + update.pos = true + update.vel = true + else + -- Direction change detected + if dir_changed then + vel = vector.multiply(dir, math.abs(vel.x + vel.z)) + update.vel = true + if dir.y ~= self.old_dir.y then + pos = vector.round(pos) + update.pos = true + end + end + -- Center on the rail + if dir.z ~= 0 and math.floor(pos.x + 0.5) ~= pos.x then + pos.x = math.floor(pos.x + 0.5) + update.pos = true + end + if dir.x ~= 0 and math.floor(pos.z + 0.5) ~= pos.z then + pos.z = math.floor(pos.z + 0.5) + update.pos = true + end + + -- Slow down or speed up.. + local acc = dir.y * -2.0 + + -- Get rail for corrected position + railparams = get_railparams(pos) + + -- no need to check for railparams == nil since we always make it exist. + local speed_mod = railparams.acceleration + if speed_mod and speed_mod ~= 0 then + -- Try to make it similar to the original carts mod + acc = acc + speed_mod + else + acc = acc - 0.4 + end + + new_acc = vector.multiply(dir, acc) + end + + -- Limits + local max_vel = carts.speed_max + for _, v in pairs({"x","y","z"}) do + if math.abs(vel[v]) > max_vel then + vel[v] = carts:get_sign(vel[v]) * max_vel + new_acc[v] = 0 + update.vel = true + end + end + + self.object:set_acceleration(new_acc) + self.old_pos = vector.round(pos) + if not vector.equals(dir, {x=0, y=0, z=0}) then + self.old_dir = vector.new(dir) + end + self.old_switch = switch_keys + + if self.punched then + self.punched = false + update.vel = true + end + + railparams = railparams or get_railparams(pos) + + if not (update.vel or update.pos) then + rail_on_step_event(railparams.on_step, self, dtime) + return + end + + local yaw = 0 + if self.old_dir.x < 0 then + yaw = math.pi/2*3 + elseif self.old_dir.x > 0 then + yaw = math.pi/2 + elseif self.old_dir.z < 0 then + yaw = math.pi + end + --self.object:set_yaw(yaw * math.pi) + + local pitch = 0 + if self.old_dir.z ~= 0 then + if dir.y == -1 then + pitch = -math.pi/4 + elseif dir.y == 1 then + pitch = math.pi/4 + end + else + if dir.y == -1 then + pitch = math.pi/4 + elseif dir.y == 1 then + pitch = -math.pi/4 + end + end + self.object:set_rotation({x = pitch, y = yaw, z = 0}) + + -- cart position correction on slopes + if pitch ~= 0 then + pos.y = pos.y + 0.5 + update.pos = true + vel = vector.divide(vel, 2) + update.vel = true + elseif self.old_pitch ~= 0 then + vel = vector.multiply(vel, 2) + update.vel = true + end + self.old_pitch = pitch + + if update.vel then + self.object:set_velocity(vel) + end + if update.pos then + if dir_changed then + self.object:set_pos(pos) + else + self.object:move_to(pos) + end + end + + -- call event handler + rail_on_step_event(railparams.on_step, self, dtime) +end + +function api:on_step(dtime) + rail_on_step(self, dtime) + rail_sound(self, dtime) +end + +return api diff --git a/minecart/cart_lib2e.lua b/minecart/cart_lib2e.lua new file mode 100644 index 0000000..cea4700 --- /dev/null +++ b/minecart/cart_lib2e.lua @@ -0,0 +1,150 @@ +--[[ + + Minecart + ======== + + Copyright (C) 2019-2020 Joachim Stolberg + + MIT + See license.txt for more information + + Cart library functions for entity based carts (level 2) + +]]-- + +-- for lazy programmers +local M = minetest.get_meta +local S = minecart.S +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local S2P = minetest.string_to_pos +local MP = minetest.get_modpath("minecart") + +local api = dofile(MP.."/cart_lib3.lua") + +-- Add node, set metadata, and load carge +local function add_cart(pos, node_name, param2, owner, userID, cargo) + local obj = minetest.add_entity(pos, node_name) + local myID = api.get_object_id(obj) + if myID then + -- Copy item data to cart entity + local entity = obj:get_luaentity() + entity.owner = owner + entity.userID = userID + entity.cargo = cargo + entity.myID = myID + obj:set_nametag_attributes({color = "#FFFF00", text = owner..": "..userID}) + minecart.add_to_monitoring(obj, myID, owner, userID) + return myID + else + print("Entity has no ID") + end +end + +function api.stop_cart(pos, entity, node_name, param2) + -- Stop sound + if entity.sound_handle then + minetest.sound_stop(entity.sound_handle) + entity.sound_handle = nil + end + minecart.stop_cart(pos, entity.myID) +end + + +-- Player adds the node +function api.add_cart(itemstack, placer, pointed_thing, node_name) + local owner = placer:get_player_name() + local meta = placer:get_meta() + local param2 = minetest.dir_to_facedir(placer:get_look_dir()) + local userID = 0 + local cargo = {} + + -- Add node + if carts:is_rail(pointed_thing.under) then + add_cart(pointed_thing.under, node_name, param2, owner, userID, cargo) + meta:set_string("cart_pos", P2S(pointed_thing.under)) + elseif carts:is_rail(pointed_thing.above) then + add_cart(pointed_thing.above, node_name, param2, owner, userID, cargo) + meta:set_string("cart_pos", P2S(pointed_thing.above)) + else + return + end + + minetest.sound_play({name = "default_place_node_metal", gain = 0.5}, + {pos = pointed_thing.above}) + + if not (creative and creative.is_enabled_for + and creative.is_enabled_for(placer:get_player_name())) then + itemstack:take_item() + end + + minetest.show_formspec(owner, "minecart:userID_entity", + "size[4,3]" .. + "label[0,0;Enter cart number:]" .. + "field[1,1;3,1;userID;;]" .. + "button_exit[1,2;2,1;exit;Save]") + + return itemstack +end + +-- Player removes the node +function api.remove_cart(self, pos, player) + -- Add cart to player inventory + local inv = player:get_inventory() + if not (creative and creative.is_enabled_for + and creative.is_enabled_for(player:get_player_name())) + or not inv:contains_item("main", "minecart:cart") then + local leftover = inv:add_item("main", "minecart:cart") + -- If no room in inventory add a replacement cart to the world + if not leftover:is_empty() then + minetest.add_item(pos, leftover) + end + end + minecart.remove_from_monitoring(self.myID) + self.object:remove() + -- Stop sound + if self.sound_handle then + minetest.sound_stop(self.sound_handle) + self.sound_handle = nil + end + return true +end + +function api.load_cargo(self, pos) + self.cargo = self.cargo or {} + for _, obj_ in pairs(minetest.get_objects_inside_radius(pos, 1)) do + local entity = obj_:get_luaentity() + if not obj_:is_player() and entity and entity.name == "__builtin:item" then + obj_:remove() + self.cargo[#self.cargo + 1] = entity.itemstring + end + end +end + +function api.unload_cargo(self, pos) + -- Spawn loaded items again + for _,item in ipairs(self.cargo or {}) do + minetest.add_item(pos, ItemStack(item)) + end + self.cargo = {} +end + +-- in the case the owner punches the cart +function api.add_cargo_to_player_inv(self, pos, puncher) + local added = false + local inv = puncher:get_inventory() + for _, obj in pairs(minetest.get_objects_inside_radius(pos, 1)) do + local entity = obj:get_luaentity() + if not obj:is_player() and entity and entity.name == "__builtin:item" then + obj:remove() + local item = ItemStack(entity.itemstring) + local leftover = inv:add_item("main", item) + if leftover:get_count() > 0 then + minetest.add_item(pos, leftover) + end + added = true -- don't dig the cart + end + end + return added +end + +return api diff --git a/minecart/cart_lib2n.lua b/minecart/cart_lib2n.lua new file mode 100644 index 0000000..4625491 --- /dev/null +++ b/minecart/cart_lib2n.lua @@ -0,0 +1,198 @@ +--[[ + + Minecart + ======== + + Copyright (C) 2019-2020 Joachim Stolberg + + MIT + See license.txt for more information + + Cart library functions for node based carts (level 2) + +]]-- + +-- for lazy programmers +local M = minetest.get_meta +local S = minecart.S +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local S2P = minetest.string_to_pos +local MP = minetest.get_modpath("minecart") + +local api = dofile(MP.."/cart_lib3.lua") + +-- Add node, set metadata, and load carge +local function add_cart(pos, node_name, param2, owner, userID, cargo) + local ndef = minetest.registered_nodes[node_name] + local node = minetest.get_node(pos) + local meta = M(pos) + local rail = node.name + minetest.add_node(pos, {name = node_name, param2 = param2}) + meta:set_string("removed_rail", rail) + meta:set_string("owner", owner) + meta:set_string("userID", userID) + meta:set_string("infotext", minetest.get_color_escape_sequence("#FFFF00")..owner..": "..userID) + if ndef.after_place_node then + ndef.after_place_node(pos) + end + if cargo and ndef.set_cargo then + ndef.set_cargo(pos, cargo) + end +end + +-- called after punch cart +local function start_cart(pos, node_name, entity_name, puncher, dir) + -- Read node metadata + local ndef = minetest.registered_nodes[node_name] + if ndef then + local meta = M(pos) + local rail = meta:get_string("removed_rail") + local userID = meta:get_int("userID") + local cart_owner = meta:get_string("owner") + local cargo = ndef.get_cargo and ndef.get_cargo(pos) or {} + -- swap node to rail + minetest.remove_node(pos) + minetest.add_node(pos, {name = rail}) + -- Add entity + local obj = minetest.add_entity(pos, entity_name) + -- Determine ID + local myID = api.get_object_id(obj) + if myID then + -- Copy metadata to cart entity + local entity = obj:get_luaentity() + entity.owner = cart_owner + entity.userID = userID + entity.cargo = cargo + entity.myID = myID + obj:set_nametag_attributes({color = "#ffff00", text = cart_owner..": "..userID}) + minecart.add_to_monitoring(obj, myID, cart_owner, userID) + minecart.node_at_station(cart_owner, userID, nil) + -- punch cart to prevent the stopped handling + obj:punch(puncher or obj, 1, { + full_punch_interval = 1.0, + damage_groups = {fleshy = 1}, + }, dir) + return myID + else + print("Entity has no ID") + end + end +end + +function api.stop_cart(pos, entity, node_name, param2) + -- rail buffer reached? + if api.get_route_key(pos) then + -- Read entity data + local owner = entity.owner or "" + local userID = entity.userID or 0 + local cargo = entity.cargo or {} + -- Remove entity + minecart.remove_from_monitoring(entity.myID) + minecart.node_at_station(owner, userID, pos) + entity.object:remove() + -- Add cart node + add_cart(pos, node_name, param2, owner, userID, cargo) + end + -- Stop sound + if entity.sound_handle then + minetest.sound_stop(entity.sound_handle) + entity.sound_handle = nil + end +end + +-- Player adds the node +function api.add_cart(itemstack, placer, pointed_thing, node_name) + local owner = placer:get_player_name() + local meta = placer:get_meta() + local param2 = minetest.dir_to_facedir(placer:get_look_dir()) + local userID = 0 + local cargo = {} + + -- Add node + if carts:is_rail(pointed_thing.under) then + add_cart(pointed_thing.under, node_name, param2, owner, userID, cargo) + meta:set_string("cart_pos", P2S(pointed_thing.under)) + elseif carts:is_rail(pointed_thing.above) then + add_cart(pointed_thing.above, node_name, param2, owner, userID, cargo) + meta:set_string("cart_pos", P2S(pointed_thing.above)) + else + return + end + + minetest.sound_play({name = "default_place_node_metal", gain = 0.5}, + {pos = pointed_thing.above}) + + if not (creative and creative.is_enabled_for + and creative.is_enabled_for(placer:get_player_name())) then + itemstack:take_item() + end + + minetest.show_formspec(owner, "minecart:userID_node", + "size[4,3]" .. + "label[0,0;Enter cart number:]" .. + "field[1,1;3,1;userID;;]" .. + "button_exit[1,2;2,1;exit;Save]") + + return itemstack +end + +function api.node_on_punch(pos, node, puncher, pointed_thing, entity_name, dir) + local ndef = minetest.registered_nodes[node.name] + -- Player digs cart by sneak-punch + if puncher and puncher:get_player_control().sneak then + api.remove_cart(nil, pos, puncher) + return + end + start_cart(pos, node.name, entity_name, puncher, dir) +end + +local function add_to_player_inventory(pos, player, node_name) + local inv = player:get_inventory() + if not (creative and creative.is_enabled_for + and creative.is_enabled_for(player:get_player_name())) + or not inv:contains_item("main", node_name) then + local leftover = inv:add_item("main", node_name) + -- If no room in inventory add a replacement cart to the world + if not leftover:is_empty() then + minetest.add_item(pos, leftover) + end + end +end + +-- Player removes the node +function api.remove_cart(self, pos, player) + if self then -- cart is still an entity + add_to_player_inventory(pos, player, self.node_name or "minecart:cart") + minecart.remove_from_monitoring(self.myID) + self.object:remove() + else + local node = minetest.get_node(pos) + local ndef = minetest.registered_nodes[node.name] + if ndef.can_dig and ndef.can_dig(pos, player) then + add_to_player_inventory(pos, player, node.name) + node.name = M(pos):get_string("removed_rail") + if node.name == "" then + node.name = "carts:rail" + end + minetest.remove_node(pos) + minetest.add_node(pos, node) + end + end +end + +function api.load_cargo() + -- nothing to load +end + +function api.unload_cargo() + -- nothing to unload +end + +function api.add_cargo_to_player_inv() + -- nothing to do +end + +-- needed by minecart.punch_cart and node carts +minecart.node_on_punch = api.node_on_punch + +return api \ No newline at end of file diff --git a/minecart/cart_lib3.lua b/minecart/cart_lib3.lua new file mode 100644 index 0000000..d1686e0 --- /dev/null +++ b/minecart/cart_lib3.lua @@ -0,0 +1,89 @@ +--[[ + + Minecart + ======== + + Copyright (C) 2019-2020 Joachim Stolberg + + MIT + See license.txt for more information + + Cart library base functions (level 3) + +]]-- + +-- for lazy programmers +local M = minetest.get_meta +local S = minecart.S +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local S2P = minetest.string_to_pos + +local api = {} + +function api.get_object_id(object) + for id, entity in pairs(minetest.luaentities) do + if entity.object == object then + return id + end + end +end + +function api.get_route_key(pos, player_name) + local pos1 = minetest.find_node_near(pos, 1, {"minecart:buffer"}) + if pos1 then + local meta = minetest.get_meta(pos1) + if player_name == nil or player_name == meta:get_string("owner") then + return P2S(pos1) + end + end +end + +function api.get_station_name(pos) + local pos1 = minetest.find_node_near(pos, 1, {"minecart:buffer"}) + if pos1 then + local name = M(pos1):get_string("name") + if name ~= "" then + return name + end + return "-" + end +end + +function api.load_cart(pos, vel, item) + -- Add cart to map + local obj = minetest.add_entity(pos, item.entity_name or "minecart:cart", nil) + -- Determine ID + local myID = api.get_object_id(obj) + if myID then + -- Copy item data to cart entity + local entity = obj:get_luaentity() + entity.owner = item.owner or "" + entity.userID = item.userID or 0 + entity.cargo = item.cargo or {} + entity.myID = myID + obj:set_nametag_attributes({color = "#FFFF00", text = entity.owner..": "..entity.userID}) + -- Update item data + item.owner = entity.owner + item.cargo = nil + -- Start cart + obj:set_velocity(vel) + return myID + else + print("Entity has no ID") + end +end + +function api.unload_cart(pos, vel, entity, item) + -- Copy entity data to item + item.cargo = entity.cargo + item.entity_name = entity.object:get_entity_name() + -- Remove entity from map + entity.object:remove() + -- Stop sound + if entity.sound_handle then + minetest.sound_stop(entity.sound_handle) + entity.sound_handle = nil + end +end + +return api diff --git a/minecart/doc.lua b/minecart/doc.lua index b029a6b..3bb7cb6 100644 --- a/minecart/doc.lua +++ b/minecart/doc.lua @@ -26,13 +26,15 @@ local summary_doc = table.concat({ S("1. Place your rails and build a route with two endpoints. Junctions are allowed as long as each route has its own start and endpoint."), S("2. Place a Railway Buffer at both endpoints (buffers are always needed, they store the route and timing information)."), S("3. Give both Railway Buffers unique station names, like Oxford and Cambridge."), - S("4. Drive from buffer to buffer in both directions using a Minecart(!) to record the routes (use 'right-left' keys to control the Minecart)."), - S("5. Punch the buffers to check the connection data (e.g. 'Oxford: connected to Cambridge')."), - S("6. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time."), - S("7. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters)."), - S("8. Place a Minecart in front of the buffer and check whether it starts after the configured time."), - S("9. Drop items into the Minecart and punch the cart to start it, or 'sneak+click' the Minecart to get the items back."), - S("10. Dig the empty cart with a second 'sneak+click' (as usual)."), + S("4. Place a Minecart at a buffer and give it a cart number (1..999)"), + S("5. Drive from buffer to buffer in both directions using the Minecart(!) to record the routes (use 'right-left' keys to control the Minecart)."), + S("6. Punch the buffers to check the connection data (e.g. 'Oxford: connected to Cambridge')."), + S("7. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time."), + S("8. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters)."), + S("9. Place a Minecart in front of the buffer and check whether it starts after the configured time."), + S("10. Check the cart state via the chat command: /mycart \n '' is the cart number"), + S("11. Drop items into the Minecart and punch the cart to start it, or 'sneak+click' the Minecart to get the items back."), + S("12. Dig the empty cart with a second 'sneak+click' (as usual)."), }, "\n") local cart_doc = S("Primary used to transport items. You can drop items into the Minecart and punch the cart to get started. Sneak+click the cart to get the items back") diff --git a/minecart/i18n.py b/minecart/i18n.py index 6c7ffed..b218fcf 100755 --- a/minecart/i18n.py +++ b/minecart/i18n.py @@ -1,38 +1,79 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Generate a template file for translation purposes +# Script to generate the template file and update the translation files. +# +# Copyright (C) 2019 Joachim Stolberg +# LGPLv2.1+ +# +# Copy the script into the mod root folder and adapt the last code lines to you needs. +from __future__ import print_function +import os, fnmatch, re, shutil -import os, fnmatch, re - -pattern = re.compile(r'[ \.=^\t]S\("(.+?)"\)', re.DOTALL) - +pattern_lua = re.compile(r'[ \.=^\t]S\("(.+?)"\)', re.DOTALL) +pattern_tr = re.compile(r'(.+?[^@])=(.+)') def gen_template(templ_file, lkeyStrings): lOut = [] - lkeyStrings = list(set(lkeyStrings)) lkeyStrings.sort() for s in lkeyStrings: lOut.append("%s=" % s) - file(templ_file, "wt").write("\n".join(lOut)) + open(templ_file, "wt").write("\n".join(lOut)) -def read_strings(fname): +def read_lua_file_strings(lua_file): lOut = [] - text = file(fname).read() - for s in pattern.findall(text): + text = open(lua_file).read() + for s in pattern_lua.findall(text): + s = re.sub(r'"\.\.\s+"', "", s) + s = re.sub("@[^@=n]", "@@", s) + s = s.replace("\n", "@n") + s = s.replace("\\n", "@n") + s = s.replace("=", "@=") lOut.append(s) return lOut -def i18n(templ_file): +def inport_tr_file(tr_file): + dOut = {} + if os.path.exists(tr_file): + for line in open(tr_file, "r").readlines(): + s = line.strip() + if s == "" or s[0] == "#": + continue + match = pattern_tr.match(s) + if match: + dOut[match.group(1)] = match.group(2) + return dOut + +def generate_template(templ_file): lOut = [] for root, dirs, files in os.walk('./'): for name in files: if fnmatch.fnmatch(name, "*.lua"): fname = os.path.join(root, name) - print fname - lOut.extend(read_strings(fname)) + found = read_lua_file_strings(fname) + print(fname, len(found)) + lOut.extend(found) + lOut = list(set(lOut)) + lOut.sort() gen_template(templ_file, lOut) + return lOut + +def update_tr_file(lNew, mod_name, tr_file): + lOut = ["# textdomain: %s\n" % mod_name] + if os.path.exists(tr_file): + shutil.copyfile(tr_file, tr_file+".old") + dOld = inport_tr_file(tr_file) + for key in lNew: + val = dOld.get(key, "") + lOut.append("%s=%s" % (key, val)) + lOut.append("##### not used anymore #####") + for key in dOld: + if key not in lNew: + lOut.append("%s=%s" % (key, dOld[key])) + open(tr_file, "w").write("\n".join(lOut)) -i18n("./locale/template.txt") -print "Done.\n" +data = generate_template("./locale/template.txt") +update_tr_file(data, "minecart", "./locale/minecart.de.tr") +print("Done.\n") + diff --git a/minecart/init.lua b/minecart/init.lua index 3882bbb..a158230 100644 --- a/minecart/init.lua +++ b/minecart/init.lua @@ -13,7 +13,7 @@ minecart = {} -- Version for compatibility checks, see readme.md/history -minecart.version = 1.05 +minecart.version = 1.06 minecart.hopper_enabled = minetest.settings:get_bool("minecart_hopper_enabled") ~= false @@ -23,11 +23,11 @@ minecart.S = minetest.get_translator("minecart") local MP = minetest.get_modpath("minecart") dofile(MP.."/storage.lua") dofile(MP.."/lib.lua") -dofile(MP.."/routes.lua") -dofile(MP.."/cart_entity.lua") +dofile(MP.."/monitoring.lua") +dofile(MP.."/recording.lua") +dofile(MP.."/minecart.lua") dofile(MP.."/buffer.lua") dofile(MP.."/protection.lua") -dofile(MP.."/cart_api.lua") if minecart.hopper_enabled then dofile(MP.."/hopper.lua") diff --git a/minecart/lib.lua b/minecart/lib.lua index dea1d94..da67ccc 100644 --- a/minecart/lib.lua +++ b/minecart/lib.lua @@ -51,6 +51,11 @@ function minecart.register_cart_names(cart_name_stopped, cart_name_running) end end +function minecart.stopped(vel, tolerance) + tolerance = tolerance or 0.05 + return math.abs(vel.x) < tolerance and math.abs(vel.z) < tolerance +end + local function is_air_like(name) local ndef = minetest.registered_nodes[name] if ndef and ndef.buildable_to then @@ -65,6 +70,17 @@ function minecart.get_next_node(pos, param2) return pos2, node end +local function get_cart_object(pos, radius) + for _, object in pairs(minetest.get_objects_inside_radius(pos, radius or 0.5)) do + if tValidCartEntities[object:get_entity_name()] then + local vel = object:get_velocity() + if vector.equals(vel, {x=0, y=0, z=0}) then -- still standing? + return object + end + end + end +end + -- check if cart can be pushed function minecart.check_cart_for_pushing(pos, param2, radius) local pos2 = param2 and vector.add(pos, param2_to_dir[param2]) or pos @@ -73,17 +89,7 @@ function minecart.check_cart_for_pushing(pos, param2, radius) return true end - for _, object in pairs(minetest.get_objects_inside_radius(pos2, radius or 0.5)) do - --print(object:get_entity_name(), tValidCartEntities[object:get_entity_name()]) - if tValidCartEntities[object:get_entity_name()] then - local vel = object:get_velocity() - if vector.equals(vel, {x=0, y=0, z=0}) then -- still standing? - return true - end - end - end - - return false + return get_cart_object(pos2, radius) ~= nil end -- check if cargo can be loaded @@ -139,7 +145,7 @@ function minecart.take_items(pos, param2, num) local owner = M(pos):get_string("owner") local inv = minetest.get_inventory({type="node", pos=npos}) - if def and inv and (not def.allow_take or def.allow_take(npos, nil, owner)) then + if def and inv and def.take_listname and (not def.allow_take or def.allow_take(npos, nil, owner)) then return minecart.inv_take_items(inv, def.take_listname, num) else local ndef = minetest.registered_nodes[node.name] @@ -155,7 +161,7 @@ function minecart.put_items(pos, param2, stack) local owner = M(pos):get_string("owner") local inv = minetest.get_inventory({type="node", pos=npos}) - if def and inv and (not def.allow_put or def.allow_put(npos, stack, owner)) then + if def and inv and def.put_listname and (not def.allow_put or def.allow_put(npos, stack, owner)) then local leftover = inv:add_item(def.put_listname, stack) if leftover:get_count() > 0 then return leftover @@ -206,14 +212,12 @@ function minecart.punch_cart(pos, param2, radius, dir) return true end - for _, object in pairs(minetest.get_objects_inside_radius(pos2, radius or 0.5)) do - if tValidCartEntities[object:get_entity_name()] then - object:punch(object, 1.0, { - full_punch_interval = 1.0, - damage_groups = {fleshy = 1}, - }, dir) - break -- start only one cart - end + local obj = get_cart_object(pos2, radius) + if obj then + obj:punch(obj, 1.0, { + full_punch_interval = 1.0, + damage_groups = {fleshy = 1}, + }, dir) end end @@ -230,6 +234,44 @@ function minecart.register_inventory(node_names, def) end end +function minecart.register_cart_entity(entity_name, node_name, entity_def) + entity_def.velocity = {x=0, y=0, z=0} -- only used on punch + entity_def.old_dir = {x=1, y=0, z=0} -- random value to start the cart on punch + entity_def.old_pos = nil + entity_def.old_switch = 0 + entity_def.node_name = node_name + minetest.register_entity(entity_name, entity_def) + -- register node for punching + minecart.register_cart_names(node_name, entity_name) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == "minecart:userID_node" then + if fields.exit == "Save" or fields.key_enter == "true" then + local cart_pos = S2P(player:get_meta():get_string("cart_pos")) + local userID = tonumber(fields.userID) or 0 + M(cart_pos):set_int("userID", userID) + M(cart_pos):set_string("infotext", minetest.get_color_escape_sequence("#FFFF00")..player:get_player_name()..": "..userID) + minecart.node_at_station(player:get_player_name(), userID, cart_pos) + end + return true + end + if formname == "minecart:userID_entity" then + if fields.exit == "Save" or fields.key_enter == "true" then + local cart_pos = S2P(player:get_meta():get_string("cart_pos")) + local obj = get_cart_object(cart_pos) + if obj then + local entity = obj:get_luaentity() + entity.userID = tonumber(fields.userID) or 0 + obj:set_nametag_attributes({color = "#ffff00", text = entity.owner..": "..entity.userID}) + minecart.update_userID(entity.myID, entity.userID) + end + end + return true + end + return false +end) + minecart.register_inventory({"default:chest", "default:chest_open"}, { put = { listname = "main", diff --git a/minecart/locale/minecart.de.tr b/minecart/locale/minecart.de.tr index 9c7f0ac..94954c5 100644 --- a/minecart/locale/minecart.de.tr +++ b/minecart/locale/minecart.de.tr @@ -1,16 +1,17 @@ # textdomain: minecart 1. Place your rails and build a route with two endpoints. Junctions are allowed as long as each route has its own start and endpoint.=1. Baue eine Schienenstrecke mit zwei Enden. Kreuzungen sind zulässig, solange jede Route ihre eigenen Start- und Endpunkte hat. -10. Dig the empty cart with a second 'sneak+click' (as usual).=10. Klicke erneut mit gedrückter Shift-Taste auf den Wagen, um diesen zu entfernen. +10. Check the cart state via the chat command: /mycart @n '' is the cart number=Prüfe den Status des Wagen mit dem Chat Kommando: /mycart @n ist die Wagennummer +11. Drop items into the Minecart and punch the cart to start it, or 'sneak+click' the Minecart to get the items back.=11: Lege Gegenstände in ein Wagen (Taste Q) und starte dann den Wagen durch Anklicken. Klicke mit gedrückter Shift-Taste auf den Wagen, um Gegenstände wieder auszuladen. +12. Dig the empty cart with a second 'sneak+click' (as usual).=10. Klicke erneut mit gedrückter Shift-Taste auf den Wagen, um diesen zu entfernen. 2. Place a Railway Buffer at both endpoints (buffers are always needed, they store the route and timing information).=2. Platziere einen Prellbock an beide Schienenenden (Prellböcke sind zwingend notwendig, sie speichern die Routen- und Zeit-Informationen). 3. Give both Railway Buffers unique station names, like Oxford and Cambridge.=3. Gib beiden Prellböcken eindeutige Stationsnamen wie: Stuttgart und München. -4. Drive from buffer to buffer in both directions using a Minecart(!) to record the routes (use 'right-left' keys to control the Minecart).=4. Um eine Route aufzuzeichnen, fahre die Route in beide Richtungen von Prellbock zu Prellbock mit einem Minecart Wagen(!). Nutze 'links-rechts' Tasten zur Steuerung. -5. Punch the buffers to check the connection data (e.g. 'Oxford: connected to Cambridge').=5. Schlage auf die Prellböcke um die Verbindungsdaten zu prüfen (bspw.: 'München: verbunden mit Stuttgart') -6. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time.=6. Optional: Konfiguriere die Wagenwartezeit in einem oder in beiden Prellböcken. Der Wagen startet dann nach dieser Zeit automatisch. -7. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters).=7. Optional: Schütze deine Schienen mit Hilfe der Meilensteine (ein Meilenstein mindestens alle 16 Blöcke). -8. Place a Minecart in front of the buffer and check whether it starts after the configured time.=8. Platziere einen Wagen direkt vor einem Prellbock und prüfe, ob er nach der konfigurierten Zeit startet. -9. Drop items into the Minecart and punch the cart to start it, or 'sneak+click' the Minecart to get the items back.=9: Lege Gegenstände in ein Wagen (Taste Q) und starte dann den Wagen durch Anklicken. Klicke mit gedrückter Shift-Taste auf den Wagen, um Gegenstände wieder auszuladen. -A minecart running through unloaded areas, mainly used for item transportation=Ein Wagen, welcher auch durch nicht geladene Kartenbereiche fährt, primär für den Transport von Gegenständen genutzt (Lore) +4. Place a Minecart at a buffer and give it a cart number (1..999)=4. Platziere einen Minecart Wagen an einem Prellbock und gib dem Wagen eine Wagennummer (1..999) +5. Drive from buffer to buffer in both directions using the Minecart(!) to record the routes (use 'right-left' keys to control the Minecart).=5. Um eine Route aufzuzeichnen, fahre die Route in beide Richtungen von Prellbock zu Prellbock mit dem Minecart Wagen(!). Nutze 'links-rechts' Tasten zur Steuerung. +6. Punch the buffers to check the connection data (e.g. 'Oxford: connected to Cambridge').=6. Schlage auf die Prellböcke um die Verbindungsdaten zu prüfen (bspw.: 'München: verbunden mit Stuttgart') +7. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time.=7. Optional: Konfiguriere die Wagenwartezeit in einem oder in beiden Prellböcken. Der Wagen startet dann nach dieser Zeit automatisch. +8. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters).=8. Optional: Schütze deine Schienen mit Hilfe der Meilensteine (ein Meilenstein mindestens alle 16 Blöcke). +9. Place a Minecart in front of the buffer and check whether it starts after the configured time.=9. Platziere einen Wagen direkt vor einem Prellbock und prüfe, ob er nach der konfigurierten Zeit startet. Allow to dig/place rails in Minecart Landmark areas=Erlaubt dir, Schienen in Meilensteinbereichen zu setzen/zu entfernen Minecart=Minecart Minecart (Sneak+Click to pick up)=Minecart (Shift+Klick zum Entfernen des Carts) @@ -27,9 +28,10 @@ Summary=Zusammenfassung Used as buffer on both rail ends. Needed to be able to record the cart routes=Preckblöcke müssen an beiden Schienenenden platziert sein, so dass Aufzeichnungen der Strecke gemacht werden können. Used to load/unload Minecart. The Hopper can push/pull items to/from chests and drop/pickup items to/from Minecarts. To unload a Minecart place the hopper below the rail. To load the Minecart, place the hopper right next to the Minecart.=Um Wagen zu be- und entladen. Der Hopper kann Gegenstände aus Kisten Holen und legen, sowie diese in Wagen fallen lassen bzw. aus Wagen entnehmen. Um einen Wagen zu entladen, muss der Hopper unter die Schiene platziert werden. Um einen Wagen zu beladen, muss der Hopper direkt neben die Schiene platziert werden. [minecart] Area is protected!=[minecart] Bereich ist geschützt! -[minecart] Please start at a Railway Buffer!=[minecart] Bitte starte beim Prellbock! +[minecart] Cart is protected by = Wagen ist geschützt durch [minecart] Recording canceled!=[minecart] Aufzeichnung abgebrochen! [minecart] Route stored!=[minecart] Strecke gespeichert [minecart] Start route recording!=[minecart] Starte die Streckenaufzeichnung! connected to=verbunden mit +##### not used anymore ##### diff --git a/minecart/locale/template.txt b/minecart/locale/template.txt index eb95b9c..8d66077 100644 --- a/minecart/locale/template.txt +++ b/minecart/locale/template.txt @@ -1,13 +1,15 @@ 1. Place your rails and build a route with two endpoints. Junctions are allowed as long as each route has its own start and endpoint.= -10. Dig the empty cart with a second 'sneak+click' (as usual).= +10. Check the cart state via the chat command: /mycart @n '' is the cart number= +11. Drop items into the Minecart and punch the cart to start it, or 'sneak+click' the Minecart to get the items back.= +12. Dig the empty cart with a second 'sneak+click' (as usual).= 2. Place a Railway Buffer at both endpoints (buffers are always needed, they store the route and timing information).= 3. Give both Railway Buffers unique station names, like Oxford and Cambridge.= -4. Drive from buffer to buffer in both directions using a Minecart(!) to record the routes (use 'right-left' keys to control the Minecart).= -5. Punch the buffers to check the connection data (e.g. 'Oxford: connected to Cambridge').= -6. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time.= -7. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters).= -8. Place a Minecart in front of the buffer and check whether it starts after the configured time.= -9. Drop items into the Minecart and punch the cart to start it, or 'sneak+click' the Minecart to get the items back.= +4. Place a Minecart at a buffer and give it a cart number (1..999)= +5. Drive from buffer to buffer in both directions using the Minecart(!) to record the routes (use 'right-left' keys to control the Minecart).= +6. Punch the buffers to check the connection data (e.g. 'Oxford: connected to Cambridge').= +7. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time.= +8. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters).= +9. Place a Minecart in front of the buffer and check whether it starts after the configured time.= Allow to dig/place rails in Minecart Landmark areas= Minecart= Minecart (Sneak+Click to pick up)= @@ -24,7 +26,7 @@ Summary= Used as buffer on both rail ends. Needed to be able to record the cart routes= Used to load/unload Minecart. The Hopper can push/pull items to/from chests and drop/pickup items to/from Minecarts. To unload a Minecart place the hopper below the rail. To load the Minecart, place the hopper right next to the Minecart.= [minecart] Area is protected!= -[minecart] Please start at a Railway Buffer!= +[minecart] Cart is protected by = [minecart] Recording canceled!= [minecart] Route stored!= [minecart] Start route recording!= diff --git a/minecart/minecart.lua b/minecart/minecart.lua new file mode 100644 index 0000000..6918d89 --- /dev/null +++ b/minecart/minecart.lua @@ -0,0 +1,84 @@ +--[[ + + Minecart + ======== + + Copyright (C) 2019-2020 Joachim Stolberg + + MIT + See license.txt for more information + +]]-- + +local S = minecart.S +local MP = minetest.get_modpath("minecart") +local lib = dofile(MP.."/cart_lib1.lua") + +lib:init(false) + +local cart_entity = { + initial_properties = { + physical = false, -- otherwise going uphill breaks + collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, + visual = "mesh", + mesh = "carts_cart.b3d", + visual_size = {x=1, y=1}, + textures = {"carts_cart.png^minecart_cart.png"}, + static_save = false, + }, + ------------------------------------ changed + owner = nil, + ------------------------------------ changed + driver = nil, + punched = false, -- used to re-send velocity and position + velocity = {x=0, y=0, z=0}, -- only used on punch + old_dir = {x=1, y=0, z=0}, -- random value to start the cart on punch + old_pos = nil, + old_switch = 0, + railtype = nil, + cargo = {}, + on_rightclick = lib.on_rightclick, + on_activate = lib.on_activate, + on_detach_child = lib.on_detach_child, + on_punch = lib.on_punch, + on_step = lib.on_step, +} + + +minetest.register_entity("minecart:cart", cart_entity) + +minecart.register_cart_names("minecart:cart", "minecart:cart") + + +minetest.register_craftitem("minecart:cart", { + description = S("Minecart (Sneak+Click to pick up)"), + inventory_image = minetest.inventorycube("carts_cart_top.png", "carts_cart_side.png^minecart_logo.png", "carts_cart_side.png^minecart_logo.png"), + wield_image = "carts_cart_side.png", + on_place = function(itemstack, placer, pointed_thing) + -- use cart as tool + local under = pointed_thing.under + local node = minetest.get_node(under) + local udef = minetest.registered_nodes[node.name] + if udef and udef.on_rightclick and + not (placer and placer:is_player() and + placer:get_player_control().sneak) then + return udef.on_rightclick(under, node, placer, itemstack, + pointed_thing) or itemstack + end + + if not pointed_thing.type == "node" then + return + end + + return lib.add_cart(itemstack, placer, pointed_thing, "minecart:cart") + end, +}) + +minetest.register_craft({ + output = "minecart:cart", + recipe = { + {"default:steel_ingot", "default:cobble", "default:steel_ingot"}, + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + }, +}) + diff --git a/minecart/monitoring.lua b/minecart/monitoring.lua new file mode 100644 index 0000000..c433da0 --- /dev/null +++ b/minecart/monitoring.lua @@ -0,0 +1,333 @@ +--[[ + + Minecart + ======== + + Copyright (C) 2019-2020 Joachim Stolberg + + MIT + See license.txt for more information + +]]-- + +-- Some notes: +-- 1) Entity IDs are volatile. For each server restart all carts get new IDs. +-- 2) Monitoring is performed for entities only. Stopped carts in form of +-- real nodes need no monitoring. +-- 3) But nodes at startions have to call 'node_at_station' to be "visible" +-- for the chat commands + + +-- for lazy programmers +local M = minetest.get_meta +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local S2P = minetest.string_to_pos +local S = minecart.S +local MP = minetest.get_modpath("minecart") +local lib = dofile(MP.."/cart_lib3.lua") + +local CartsOnRail = minecart.CartsOnRail -- from storage.lua +local get_route = minecart.get_route -- from storage.lua +local NodesAtStation = {} + +-- +-- Helper functions +-- +local function calc_pos_and_vel(item) + if item.start_time and item.start_key then -- cart on recorded route + local run_time = minetest.get_gametime() - item.start_time + local waypoints = get_route(item.start_key).waypoints + local waypoint = waypoints[run_time] + if waypoint then + return S2P(waypoint[1]), S2P(waypoint[2]) + end + end + if item.last_pos then + item.last_pos = vector.round(item.last_pos) + if carts:is_rail(item.last_pos, minetest.raillike_group("rail")) then + return item.last_pos, item.last_vel + end + item.last_pos.y = item.last_pos.y - 1 + if carts:is_rail(item.last_pos, minetest.raillike_group("rail")) then + return item.last_pos, item.last_vel + end + end + return item.start_pos, {x=0, y=0, z=0} +end + +-- +-- Monitoring of cart entities +-- +function minecart.add_to_monitoring(obj, myID, owner, userID) + local pos = vector.round(obj:get_pos()) + CartsOnRail[myID] = { + start_key = lib.get_route_key(pos), + start_pos = pos, + owner = owner, -- needed for query API + userID = userID, -- needed for query API + stopped = true, + entity_name = obj:get_entity_name() + } +end + +-- Called after cart number formspec is closed +function minecart.update_userID(myID, userID) + if CartsOnRail[myID] then + CartsOnRail[myID].userID = userID + end +end + +-- When cart entity is removed +function minecart.remove_from_monitoring(myID) + if myID then + CartsOnRail[myID] = nil + end +end + +-- For node carts at stations +function minecart.node_at_station(owner, userID, pos) + NodesAtStation[owner] = NodesAtStation[owner] or {} + NodesAtStation[owner][userID] = pos +end + +function minecart.start_cart(pos, myID) + local item = CartsOnRail[myID] + if item and item.stopped then + item.stopped = false + item.start_pos = pos + -- cart started from a buffer? + local start_key = lib.get_route_key(pos) + if start_key then + item.start_time = minetest.get_gametime() + item.start_key = start_key + item.junctions = minecart.get_route(start_key).junctions + return true + end + end + return false +end + +function minecart.stop_cart(pos, myID) + local item = CartsOnRail[myID] + if item and not item.stopped then + item.start_time = nil + item.start_key = nil + item.start_pos = nil + item.junctions = nil + item.stopped = true + return true + end + return false +end + +local function monitoring() + local to_be_added = {} + for key, item in pairs(CartsOnRail) do + local entity = minetest.luaentities[key] + --print("Cart:", key, item.owner, item.myID, item.userID, item.stopped) + if entity then -- cart entity running + local pos = entity.object:get_pos() + local vel = entity.object:get_velocity() + if not minetest.get_node_or_nil(pos) then -- unloaded area + lib.unload_cart(pos, vel, entity, item) + item.stopped = minecart.stopped(vel) + end + -- store last pos from cart + item.last_pos, item.last_vel = pos, vel + else -- no cart running + local pos, vel = calc_pos_and_vel(item) + if pos and vel then + if minetest.get_node_or_nil(pos) then -- loaded area + local myID = lib.load_cart(pos, vel, item) + if myID then + item.stopped = minecart.stopped(vel) + to_be_added[myID] = table.copy(item) + CartsOnRail[key] = nil -- invalid old ID + end + end + item.last_pos, item.last_vel = pos, vel + else + CartsOnRail[key] = nil + end + end + end + -- table maintenance + for key,val in pairs(to_be_added) do + CartsOnRail[key] = val + end + minetest.after(1, monitoring) +end +-- delay the start to prevent cart disappear into nirvana +minetest.register_on_mods_loaded(function() + minetest.after(10, monitoring) +end) + + +-- +-- API functions +-- + +-- Return a list of carts with current position and speed. +function minecart.get_cart_list() + local tbl = {} + for id, item in pairs(CartsOnRail) do + local pos, speed = calc_pos_and_vel(item) + tbl[#tbl+1] = {pos = pos, speed = speed, id = id} + end + return tbl +end + +local function get_cart_pos(query_pos, cart_pos) + local dist = math.floor(vector.distance(cart_pos, query_pos)) + local station = lib.get_station_name(cart_pos) + return station or dist +end + +local function get_cart_state(name, userID) + for id, item in pairs(CartsOnRail) do + if item.owner == name and item.userID == userID then + return item.stopped and "stopped" or "running", item.last_pos + end + end + return nil, nil +end + +minetest.register_chatcommand("mycart", { + params = "", + description = "Output cart state and position, or a list of carts, if no cart number is given.", + func = function(name, param) + local userID = tonumber(param) + local query_pos = minetest.get_player_by_name(name):get_pos() + + if userID then + -- First check if it is a node cart at a station + local cart_pos = NodesAtStation[name] and NodesAtStation[name][userID] + if cart_pos then + local pos = get_cart_pos(query_pos, cart_pos) + return true, "Cart #"..userID.." stopped at "..pos.." " + end + -- Check all running carts + local state, cart_pos = get_cart_state(name, userID) + if state then + local pos = get_cart_pos(query_pos, cart_pos) + if type(pos) == "string" then + return true, "Cart #"..userID.." stopped at "..pos.." " + elseif state == "running" then + return true, "Cart #"..userID.." running "..pos.." m away " + else + return true, "Cart #"..userID.." stopped "..pos.." m away " + end + end + return false, "Cart #"..userID.." is unknown" + else + -- Output a list with all numbers + local tbl = {} + for userID, pos in pairs(NodesAtStation[name] or {}) do + tbl[#tbl + 1] = userID + end + for id, item in pairs(CartsOnRail) do + if item.owner == name then + tbl[#tbl + 1] = item.userID + end + end + return true, "List of carts: "..table.concat(tbl, ", ").." " + end + end +}) + +function minecart.cmnd_cart_state(name, userID) + -- First check if it is a node cart at a station + local pos = NodesAtStation[name] and NodesAtStation[name][userID] + if pos then + return "stopped" + end + return get_cart_state(name, userID) +end + +function minecart.cmnd_cart_location(name, userID, query_pos) + -- First check if it is a node cart at a station + local station = NodesAtStation[name] and NodesAtStation[name][userID] + if station then + return station + end + local state, cart_pos = get_cart_state(name, userID) + if state then + return get_cart_pos(query_pos, cart_pos) + end +end + +minetest.register_on_mods_loaded(function() + if minetest.global_exists("techage") then + techage.icta_register_condition("cart_state", { + title = "read cart state", + formspec = { + { + type = "digits", + name = "number", + label = "cart number", + default = "", + }, + { + type = "label", + name = "lbl", + label = "Read state from one of your carts", + }, + }, + button = function(data, environ) -- default button label + local number = tonumber(data.number) or 0 + return 'cart_state('..number..')' + end, + code = function(data, environ) + local s = 'minecart.cmnd_cart_state("%s", %u)' + local number = tonumber(data.number) or 0 + return string.format(s, environ.owner, number), "~= 0" + end, + }) + techage.icta_register_condition("cart_location", { + title = "read cart location", + formspec = { + { + type = "digits", + name = "number", + label = "cart number", + default = "", + }, + { + type = "label", + name = "lbl", + label = "Read location from one of your carts", + }, + }, + button = function(data, environ) -- default button label + local number = tonumber(data.number) or 0 + return 'cart_loc('..number..')' + end, + code = function(data, environ) + local s = 'minecart.cmnd_cart_location("%s", %u, env.pos)' + local number = tonumber(data.number) or 0 + return string.format(s, environ.owner, number), "~= 0" + end, + }) + techage.lua_ctlr.register_function("cart_state", { + cmnd = function(self, num) + num = tonumber(num) or 0 + return minecart.cmnd_cart_state(self.meta.owner, num) + end, + help = " $cart_state(num)\n".. + " Read state from one of your carts.\n".. + ' "num" is the cart number\n'.. + ' example: sts = $cart_state(2)' + }) + techage.lua_ctlr.register_function("cart_location", { + cmnd = function(self, num) + num = tonumber(num) or 0 + return minecart.cmnd_cart_location(self.meta.owner, num, self.meta.pos) + end, + help = " $cart_location(num)\n".. + " Read location from one of your carts.\n".. + ' "num" is the cart number\n'.. + ' example: sts = $cart_location(2)' + }) + end +end) + diff --git a/minecart/recording.lua b/minecart/recording.lua new file mode 100644 index 0000000..4034ff9 --- /dev/null +++ b/minecart/recording.lua @@ -0,0 +1,89 @@ +--[[ + + Minecart + ======== + + Copyright (C) 2019-2020 Joachim Stolberg + + MIT + See license.txt for more information + +]]-- + +-- for lazy programmers +local M = minetest.get_meta +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local S2P = minetest.string_to_pos +local S = minecart.S +local MP = minetest.get_modpath("minecart") +local lib = dofile(MP.."/cart_lib3.lua") + +local CartsOnRail = minecart.CartsOnRail -- from storage.lua +local get_route = minecart.get_route -- from storage.lua + +-- +-- Route recording +-- +function minecart.start_recording(self, pos) + self.start_key = lib.get_route_key(pos, self.driver) + if self.start_key then + self.waypoints = {} + self.junctions = {} + self.recording = true + self.next_time = minetest.get_us_time() + 1000000 + minetest.chat_send_player(self.driver, S("[minecart] Start route recording!")) + end +end + +function minecart.store_next_waypoint(self, pos, vel) + if self.start_key and self.recording and self.driver and + self.next_time < minetest.get_us_time() then + self.next_time = minetest.get_us_time() + 1000000 + self.waypoints[#self.waypoints+1] = {P2S(vector.round(pos)), P2S(vector.round(vel))} + elseif self.recording and not self.driver then + self.recording = false + self.waypoints = nil + self.junctions = nil + end +end + +-- destination reached(speed == 0) +function minecart.stop_recording(self, pos, vel, puncher) + local dest_pos = lib.get_route_key(pos, self.driver) + if dest_pos then + if self.start_key ~= dest_pos then + local route = { + waypoints = self.waypoints, + dest_pos = dest_pos, + junctions = self.junctions, + } + minecart.store_route(self.start_key, route) + minetest.chat_send_player(self.driver, S("[minecart] Route stored!")) + else + minetest.chat_send_player(self.driver, S("[minecart] Recording canceled!")) + end + else + minetest.chat_send_player(self.driver, S("[minecart] Recording canceled!")) + end + self.recording = false + self.waypoints = nil + self.junctions = nil +end + +function minecart.set_junction(self, pos, dir, switch_keys) + if self.junctions then + self.junctions[P2S(vector.round(pos))] = {dir, switch_keys} + end +end + +function minecart.get_junction(self, pos, dir) + local junctions = CartsOnRail[self.myID] and CartsOnRail[self.myID].junctions + if junctions then + local data = junctions[P2S(vector.round(pos))] + if data then + return data[1], data[2] + end + end + return dir +end + diff --git a/minecart/routes.lua b/minecart/routes.lua deleted file mode 100644 index 556286f..0000000 --- a/minecart/routes.lua +++ /dev/null @@ -1,291 +0,0 @@ ---[[ - - Minecart - ======== - - Copyright (C) 2019-2020 Joachim Stolberg - - MIT - See license.txt for more information - -]]-- - --- for lazy programmers -local M = minetest.get_meta -local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end -local S2P = minetest.string_to_pos -local S = minecart.S - -local CartsOnRail = minecart.CartsOnRail - --- --- Helper functions --- -local function get_object_id(object) - for id, entity in pairs(minetest.luaentities) do - if entity.object == object then - return id - end - end -end - -local function get_route_key(pos, player_name) - local pos1 = minetest.find_node_near(pos, 1, {"minecart:buffer"}) - if pos1 then - local meta = minetest.get_meta(pos1) - if player_name == nil or player_name == meta:get_string("owner") then - return P2S(pos1) - end - end -end - --- --- Recording --- -function minecart.add_cart_to_monitoring(obj, owner, cargo) - --print("add_cart_to_monitoring", dump(cargo)) - local self = obj:get_luaentity() - self.myID = get_object_id(obj) - self.owner = owner - local pos = self.object:get_pos() - CartsOnRail[self.myID] = { - start_key = get_route_key(pos), - start_pos = pos, - stopped = true, - owner = owner, - entity_name = self.name, - cargo = cargo, - } - return self.myID -end - -function minecart.start_recording(self, pos, vel, puncher) - -- Player punches cart to start the trip - if puncher:get_player_name() == self.driver and vector.equals(vel, {x=0, y=0, z=0}) then - self.start_key = get_route_key(pos, self.driver) - if self.start_key then - self.waypoints = {} - self.junctions = {} - self.recording = true - self.next_time = minetest.get_us_time() + 1000000 - minetest.chat_send_player(self.driver, S("[minecart] Start route recording!")) - end - end -end - -function minecart.store_next_waypoint(self, pos, vel) - if self.start_key and self.recording and self.driver and - self.next_time < minetest.get_us_time() then - self.next_time = minetest.get_us_time() + 1000000 - self.waypoints[#self.waypoints+1] = {P2S(vector.round(pos)), P2S(vector.round(vel))} - - local dest_pos = get_route_key(pos, self.driver) - if vector.equals(vel, {x=0, y=0, z=0}) and dest_pos then - if self.start_key ~= dest_pos then - local route = { - waypoints = self.waypoints, - dest_pos = dest_pos, - junctions = self.junctions, - } - minecart.store_route(self.start_key, route) - minetest.chat_send_player(self.driver, S("[minecart] Route stored!")) - else - minetest.chat_send_player(self.driver, S("[minecart] Recording canceled!")) - end - self.recording = false - self.waypoints = nil - self.junctions = nil - end - elseif self.recording and not self.driver then - self.recording = false - self.waypoints = nil - self.junctions = nil - end -end - -function minecart.set_junction(self, pos, dir, switch_keys) - local junctions = CartsOnRail[self.myID] and CartsOnRail[self.myID].junctions - if junctions then - if self.junctions then - self.junctions[minetest.pos_to_string(vector.round(pos))] = {dir, switch_keys} - end - end -end - -function minecart.get_junction(self, pos, dir) - local junctions = CartsOnRail[self.myID] and CartsOnRail[self.myID].junctions - if junctions then - local data = junctions[minetest.pos_to_string(vector.round(pos))] - if data then - return data[1], data[2] - end - end - return dir -end - --- --- Normal operation --- -function minecart.start_run(self, pos, vel, driver) - if vector.equals(vel, {x=0, y=0, z=0}) then - local start_key = get_route_key(pos) - if not start_key then - if driver then -- Punched from inside the cart - -- Don't start the cart - self.velocity = {x=0, y=0, z=0} - minetest.chat_send_player(driver, S("[minecart] Please start at a Railway Buffer!")) - return - end - -- Add also carts without route to be able to restore last pos/vel - minetest.log("info", "[minecart] Cart "..self.myID.." started.") - --print("start_run", dump(CartsOnRail[self.myID])) - CartsOnRail[self.myID].stopped = false - else -- Add cart to monitoring - minetest.log("info", "[minecart] Cart "..self.myID.." started.") - --print("start_run", dump(CartsOnRail[self.myID])) - local item = CartsOnRail[self.myID] - item.start_time = minetest.get_gametime() - item.start_key = start_key - item.stopped = false - item.junctions = minecart.get_route(start_key).junctions - end - end -end - -function minecart.attach_cargo(self, pos) - local data = CartsOnRail[self.myID] - if data then - data.attached_items = {} - for _, obj_ in pairs(minetest.get_objects_inside_radius(pos, 1)) do - local entity = obj_:get_luaentity() - if not obj_:is_player() and entity and entity.name == "__builtin:item" then - obj_:remove() - data.attached_items[#data.attached_items + 1] = entity.itemstring - end - end - end -end - -function minecart.detach_cargo(self, pos, data) - -- Spawn loaded items again - if data.attached_items then - for _,item in ipairs(data.attached_items) do - minetest.add_item(pos, ItemStack(item)) - end - end -end - -function minecart.stopped(self, pos) - local data = CartsOnRail[self.myID] - if data and not data.stopped then - minecart.detach_cargo(self, pos, data) - data.stopped = true - data.start_key = get_route_key(pos) - data.start_pos = pos - data.start_time = nil - minetest.log("info", "[minecart] Cart "..self.myID.." stopped.") - if self.sound_handle then - minetest.sound_stop(self.sound_handle) - end - return data.cargo or {} -- for node based carts - end -end - -function minecart.on_dig(self) - if self and self.myID then - CartsOnRail[self.myID] = nil - end -end - --- --- Monitoring --- -local function spawn_cart(pos, vel, item) --- local node = minetest.get_node(pos) --- if not minetest.tValidCarts[node.name] then - local pos2 = vector.round(pos) - if carts:is_rail(pos2) or carts:is_rail({x = pos2.x, y = pos2.y-1, z = pos2.z}) then - local obj = minetest.add_entity(pos, item.entity_name or "minecart:cart", nil) - obj:set_velocity(vel) - local id = minecart.add_cart_to_monitoring(obj, item.owner) - minetest.log("info", "[minecart] Cart "..id.." spawned again.") - return id - end -end - -local function calc_pos_and_vel(item) - if item.start_time and item.start_key then - local run_time = minetest.get_gametime() - item.start_time - local waypoints = minecart.get_route(item.start_key).waypoints - local waypoint = waypoints[run_time] - if waypoint then - return minetest.string_to_pos(waypoint[1]), minetest.string_to_pos(waypoint[2]) - end - end - if item.last_pos then - return item.last_pos, item.last_vel - end - return item.start_pos, {x=0, y=0, z=0} -end - -local function monitoring() - local to_be_added = {} - for key, item in pairs(CartsOnRail) do - --print("Cart:", key, P2S(item.start_pos), item.owner) - if not item.recording then - local entity = minetest.luaentities[key] - if entity then -- cart in loaded area - local pos = entity.object:get_pos() - local vel = entity.object:get_velocity() - if not minetest.get_node_or_nil(pos) then -- in unloaded area - minetest.log("info", "[minecart] Cart "..key.." virtualized.") - if entity.sound_handle then - minetest.sound_stop(entity.sound_handle) - end - if vector.equals(vel, {x=0, y=0, z=0}) then - minecart.attach_cargo(entity, pos) - end - entity.object:remove() - end - -- store last pos from cart without route - item.last_pos, item.last_vel = pos, vel - else -- cart in unloaded area - local pos, vel = calc_pos_and_vel(item) - if pos and vel then - if minetest.get_node_or_nil(pos) then -- in loaded area - local id = spawn_cart(pos, vel, item) - if id then - to_be_added[id] = table.copy(item) - CartsOnRail[key] = nil - end - end - else - CartsOnRail[key] = nil - end - end - end - end - -- table maintenance - for key,val in pairs(to_be_added) do - CartsOnRail[key] = val - end - minetest.after(1, monitoring) -end -minetest.after(1, monitoring) - -minecart.calc_pos_and_vel = calc_pos_and_vel - --- --- API function to get a list of cart data with current position and speed. --- -function minecart.get_cart_list() - local tbl = {} - for id, item in pairs(CartsOnRail) do - local pos, speed = calc_pos_and_vel(item) - tbl[#tbl+1] = {pos = pos, speed = speed, id = id} - end - return tbl -end - --- minecart.get_route_key(pos, player_name) -minecart.get_route_key = get_route_key diff --git a/signs_bot/cmd_move.lua b/signs_bot/cmd_move.lua index 7245999..0f7fccf 100644 --- a/signs_bot/cmd_move.lua +++ b/signs_bot/cmd_move.lua @@ -279,6 +279,9 @@ signs_bot.register_botcommand("pause", { mem.steps = nil return signs_bot.DONE end + if mem.capa then + mem.capa = mem.capa + 1 + end return signs_bot.BUSY end, }) @@ -289,6 +292,9 @@ signs_bot.register_botcommand("stop", { num_param = 0, description = I("Stop the robot."), cmnd = function(base_pos, mem, slot) + if mem.capa then + mem.capa = mem.capa + 2 + end return signs_bot.DONE end, }) diff --git a/signs_bot/cmd_place.lua b/signs_bot/cmd_place.lua index 1cb6703..cc09820 100644 --- a/signs_bot/cmd_place.lua +++ b/signs_bot/cmd_place.lua @@ -203,7 +203,7 @@ signs_bot.register_botcommand("place_above", { local function dig_item(base_pos, robot_pos, param2, slot, route, level) local pos1 = lib.dest_pos(robot_pos, param2, route) - pos1.y = pos1.y + level + pos1.y = pos1.y + (level or 0) local node = lib.get_node_lvm(pos1) local dug_name = lib.is_simple_node(node) if not lib.not_protected(base_pos, pos1) then diff --git a/signs_bot/commands.lua b/signs_bot/commands.lua index eed8f81..5bc559b 100644 --- a/signs_bot/commands.lua +++ b/signs_bot/commands.lua @@ -233,6 +233,8 @@ local function move(mem, any_sensor) activate_sensor(mem.robot_pos, (mem.robot_param2 + 1) % 4) activate_sensor(mem.robot_pos, (mem.robot_param2 + 3) % 4) end + elseif mem.capa then + mem.capa = mem.capa + 1 end end diff --git a/signs_bot/interpreter.lua b/signs_bot/interpreter.lua index e3d92d1..aa83e58 100644 --- a/signs_bot/interpreter.lua +++ b/signs_bot/interpreter.lua @@ -41,8 +41,10 @@ end local function get_line_tokens(script) local idx = 0 - local lines = string.split(script or "" - , "\n", true) + script = script or "" + script = script:gsub("\r\n", "\n") + script = script:gsub("\r", "\n") + local lines = string.split(script, "\n", true) return function() while idx < #lines do idx = idx + 1 @@ -95,6 +97,7 @@ end local function pass1(tokens) local pc = 1 + tSymbolTbl = {} for _, token in ipairs(tokens) do if token:find("%w+:") then tSymbolTbl[token] = pc @@ -129,6 +132,9 @@ end -- Commands ------------------------------------------------------------------------------- local function register_command(cmnd_name, num_param, cmnd_func, check_func) + assert(num_param, cmnd_name..": num_param = "..dump(num_param)) + assert(cmnd_func, cmnd_name..": cmnd_func = "..dump(cmnd_func)) + assert(check_func or num_param == 0, cmnd_name..": check_func = "..dump(check_func)) lCmdLookup[#lCmdLookup + 1] = {num_param, cmnd_func, cmnd_name} tCmdDef[cmnd_name] = { num_param = num_param, @@ -174,6 +180,9 @@ register_command("call", 1, mem.Stack[#mem.Stack + 1] = mem.pc + 2 mem.pc = addr - 2 return api.DONE + end, + function(addr) + return tSymbolTbl[addr..":"] end ) @@ -192,6 +201,9 @@ register_command("jump", 1, function(base_pos, mem, addr) mem.pc = addr - 2 return api.DONE + end, + function(addr) + return tSymbolTbl[addr..":"] end ) @@ -205,14 +217,19 @@ register_command("exit", 0, -- API functions ------------------------------------------------------------------------------- -function api.register_command(cmnd_name, num_param, cmnd_func) - register_command(cmnd_name, num_param, cmnd_func) +function api.register_command(cmnd_name, num_param, cmnd_func, check_func) + register_command(cmnd_name, num_param, cmnd_func, check_func) end -- function returns: true/false, error_string, line-num function api.check_script(script) local tbl = {} local num_token = 0 + + -- to fill the symbol table + local tokens = tokenizer(script) + pass1(tokens) + for idx, cmnd, param1, param2, param3 in get_line_tokens(script) do if tCmdDef[cmnd] then num_token = num_token + 1 + tCmdDef[cmnd].num_param @@ -222,7 +239,11 @@ function api.check_script(script) param1 = tonumber(param1) or param1 param2 = tonumber(param2) or param2 param3 = tonumber(param3) or param3 - if tCmdDef[cmnd].check and not tCmdDef[cmnd].check(param1, param2, param3) then + local num_param = (param1 and 1 or 0) + (param2 and 1 or 0) + (param3 and 1 or 0) + if tCmdDef[cmnd].num_param ~= num_param then + return false, I("Wrong number of parameters"), idx + end + if tCmdDef[cmnd].num_param > 0 and not tCmdDef[cmnd].check(param1, param2, param3) then return false, I("Parameter error"), idx end elseif not cmnd:find("%w+:") then @@ -275,4 +296,4 @@ function api.reset_script(base_pos, mem) mem.Stack = {} end -return api \ No newline at end of file +return api diff --git a/signs_bot/lib.lua b/signs_bot/lib.lua index 233da0e..459db8f 100644 --- a/signs_bot/lib.lua +++ b/signs_bot/lib.lua @@ -141,6 +141,7 @@ end -- Check rights before node is dug or inventory is used function signs_bot.lib.not_protected(base_pos, pos) local me = M(base_pos):get_string("owner") + minetest.load_area(pos) if minetest.is_protected(pos, me) then return false end diff --git a/techage/README.md b/techage/README.md index 50c90ea..7f30632 100644 --- a/techage/README.md +++ b/techage/README.md @@ -35,7 +35,7 @@ Textures: CC BY-SA 3.0 ### Dependencies Required: default, doors, bucket, stairs, screwdriver, basic_materials, tubelib2, minecart, lcdlib, safer_lua Recommended: signs_bot, hyperloop, compost, techpack_stairway, autobahn -Optional: unified_inventory, wielded_light, unifieddyes, lua-mashal, lsqlite3 +Optional: unified_inventory, wielded_light, unifieddyes, lua-mashal, lsqlite3, moreores The mods `default`, `doors`, `bucket`, `stairs`, and `screwdriver` are part of Minetest Game. @@ -88,6 +88,6 @@ to 'lsqlite3' and 'lua-marshal', but there is no way back, so: - 2020-05-22 V0.08 * Support for 'lua-marshal' and 'lsqlite3' added - 2020-05-31 V0.09 * TA4 tubes upgraded, manuals updated - 2020-06-04 V0.10 * minor changes and bugfixes - - +- 2020-06-14 V0.11 * cart commands added for both controllers, support for moreores added +- 2020-06-17 V0.12 * Ethereal support added, manual correction, tin ingot recipe bugfix diff --git a/techage/basic_machines/chest.lua b/techage/basic_machines/chest.lua index 5f4e0b3..562cb39 100644 --- a/techage/basic_machines/chest.lua +++ b/techage/basic_machines/chest.lua @@ -42,7 +42,7 @@ local function can_dig(pos, player) end local function after_dig_node(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) end local function formspec2() diff --git a/techage/basic_machines/consumer.lua b/techage/basic_machines/consumer.lua index 0cc5239..53abb76 100644 --- a/techage/basic_machines/consumer.lua +++ b/techage/basic_machines/consumer.lua @@ -197,7 +197,7 @@ function techage.register_consumer(base_name, inv_name, tiles, tNode, validState if crd.power_netw then crd.power_netw:after_dig_node(pos) end - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end diff --git a/techage/basic_machines/ta4_chest.lua b/techage/basic_machines/ta4_chest.lua index 5c77176..d98e00e 100644 --- a/techage/basic_machines/ta4_chest.lua +++ b/techage/basic_machines/ta4_chest.lua @@ -436,7 +436,7 @@ local function on_rotate(pos, node, user, mode, new_param2) end local function after_dig_node(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) convert_to_chest_again(pos, oldnode, digger) end diff --git a/techage/basis/command.lua b/techage/basis/command.lua index 94b4464..12fe9d1 100644 --- a/techage/basis/command.lua +++ b/techage/basis/command.lua @@ -18,6 +18,7 @@ local S = function(pos) if pos then return minetest.pos_to_string(pos) end end --local M = minetest.get_meta local NodeInfoCache = {} +local NumbersToBeRecycled = {} local MP = minetest.get_modpath("techage") local techage_use_sqlite = minetest.settings:get_bool('techage_use_sqlite', false) @@ -210,17 +211,19 @@ function techage.add_node(pos, name) if item_handling_node(name) then Tube:after_place_node(pos) end - -- store position - return get_number(pos, true) + local key = minetest.hash_node_position(pos) + return NumbersToBeRecycled[key] or get_number(pos, true) end -- Function removes the node from the techage lists. -function techage.remove_node(pos) - local number = get_number(pos) +function techage.remove_node(pos, oldnode, oldmetadata) + local number = oldmetadata and oldmetadata.fields and oldmetadata.fields.node_number + number = number or get_number(pos) if number then + local key = minetest.hash_node_position(pos) + NumbersToBeRecycled[key] = number local ninfo = NodeInfoCache[number] or update_nodeinfo(number) if ninfo then - backend.del_nodepos(number) NodeInfoCache[number] = nil if item_handling_node(ninfo.name) then Tube:after_dig_node(pos) diff --git a/techage/basis/numbers_sqlite.lua b/techage/basis/numbers_sqlite.lua index 670768d..dcc3f6b 100644 --- a/techage/basis/numbers_sqlite.lua +++ b/techage/basis/numbers_sqlite.lua @@ -80,6 +80,9 @@ end local Version = storage:get_int("Version") or 0 local NextNumber = 0 +if Version == 0 then + Version = 4 +end if Version == 3 then Version = 4 NextNumber = storage:get_int("NextNumber") diff --git a/techage/carts/chest_cart.lua b/techage/carts/chest_cart.lua index 20e8a6c..2cd29bd 100644 --- a/techage/carts/chest_cart.lua +++ b/techage/carts/chest_cart.lua @@ -17,6 +17,10 @@ local M = minetest.get_meta local S = techage.S local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end local S2P = minetest.string_to_pos +local MP = minetest.get_modpath("minecart") +local cart = dofile(MP.."/cart_lib1.lua") + +cart:init(true) local function formspec() return "size[8,6]".. @@ -31,7 +35,8 @@ end local function can_dig(pos, player) local owner = M(pos):get_string("owner") - if owner ~= "" and owner ~= player:get_player_name() then + if owner ~= "" and (owner ~= player:get_player_name() or + not minetest.check_player_privs(player:get_player_name(), "minecart")) then return false end local inv = minetest.get_meta(pos):get_inventory() @@ -91,12 +96,11 @@ minetest.register_node("techage:chest_cart", { end, on_place = function(itemstack, placer, pointed_thing) - return minecart.node_on_place(itemstack, placer, pointed_thing, - "techage:chest_cart") + return cart.add_cart(itemstack, placer, pointed_thing, "techage:chest_cart") end, on_punch = function(pos, node, puncher, pointed_thing) - minecart.node_on_punch(pos, node, puncher, pointed_thing, "techage:chest_cart_entity") + cart.node_on_punch(pos, node, puncher, pointed_thing, "techage:chest_cart_entity") end, set_cargo = function(pos, data) @@ -133,9 +137,9 @@ minecart.register_cart_entity("techage:chest_cart_entity", "techage:chest_cart", visual_size = {x=0.66, y=0.66, z=0.66}, static_save = false, }, - on_activate = minecart.on_activate, - on_punch = minecart.on_punch, - on_step = minecart.on_step, + on_activate = cart.on_activate, + on_punch = cart.on_punch, + on_step = cart.on_step, }) techage.register_node({"techage:chest_cart"}, { diff --git a/techage/carts/tank_cart.lua b/techage/carts/tank_cart.lua index 4cc4a14..562ecec 100644 --- a/techage/carts/tank_cart.lua +++ b/techage/carts/tank_cart.lua @@ -19,6 +19,10 @@ local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end local S2P = minetest.string_to_pos local Pipe = techage.LiquidPipe local liquid = techage.liquid +local MP = minetest.get_modpath("minecart") +local cart = dofile(MP.."/cart_lib1.lua") + +cart:init(true) local CAPACITY = 100 @@ -110,8 +114,7 @@ minetest.register_node("techage:tank_cart", { end, on_place = function(itemstack, placer, pointed_thing) - return minecart.node_on_place(itemstack, placer, pointed_thing, - "techage:tank_cart") + return cart.add_cart(itemstack, placer, pointed_thing, "techage:tank_cart") end, on_punch = function(pos, node, puncher, pointed_thing) @@ -121,7 +124,7 @@ minetest.register_node("techage:tank_cart", { if techage.liquid.is_container_empty(wielded_item) then liquid.on_punch(pos, node, puncher, pointed_thing) else - minecart.node_on_punch(pos, node, puncher, pointed_thing, "techage:tank_cart_entity") + cart.node_on_punch(pos, node, puncher, pointed_thing, "techage:tank_cart_entity") end end, @@ -170,9 +173,9 @@ minecart.register_cart_entity("techage:tank_cart_entity", "techage:tank_cart", { visual_size = {x=0.66, y=0.66, z=0.66}, static_save = false, }, - on_activate = minecart.on_activate, - on_punch = minecart.on_punch, - on_step = minecart.on_step, + on_activate = cart.on_activate, + on_punch = cart.on_punch, + on_step = cart.on_step, }) minetest.register_craft({ diff --git a/techage/chemistry/ta4_doser.lua b/techage/chemistry/ta4_doser.lua index ed63184..a94eae8 100644 --- a/techage/chemistry/ta4_doser.lua +++ b/techage/chemistry/ta4_doser.lua @@ -267,7 +267,7 @@ minetest.register_node("techage:ta4_doser", { del_liquids(pos) end, after_dig_node = function(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) Pipe:after_dig_node(pos) techage.del_mem(pos) end, diff --git a/techage/doc/items.lua b/techage/doc/items.lua index b5b98cc..d41cec0 100644 --- a/techage/doc/items.lua +++ b/techage/doc/items.lua @@ -46,6 +46,7 @@ techage.Items = { ta2_rinser = "techage:ta2_rinser_pas", ta2_chest = "techage:chest_ta2", ta2_forceload = "techage:forceload", + ta2_driveaxle = "techage:axle", --------------------- techage_ta3 = "techage_ta3.png", techage_ta31 = "techage_ta3b.png", diff --git a/techage/doc/manual_DE.lua b/techage/doc/manual_DE.lua index 77af185..46852cc 100644 --- a/techage/doc/manual_DE.lua +++ b/techage/doc/manual_DE.lua @@ -26,6 +26,7 @@ techage.manual_DE.aTitel = { "3,TA2 Zylinder /Cylinder", "3,TA2 Schwungrad / Flywheel", "3,TA2 Dampfleitungen / Steam Pipe", + "3,TA2 Antriebsachsen / TA2 Drive Axle", "2,Items schieben und sortieren", "3,Röhren / TechAge Tube", "3,TA2 Schieber / Pusher", @@ -233,6 +234,7 @@ techage.manual_DE.aText = { "\n".. "\n", "Baborium wird nur im Untertagebau gewonnen. Baborium findet man nur in Stein in einer Höhe zwischen -250 und -340 Meter.\n".. + "Baborium kann nur im TA3 Industrieofen geschmolzen werden.\n".. "\n".. "\n".. "\n", @@ -377,6 +379,10 @@ techage.manual_DE.aText = { "\n".. "\n".. "\n", + "Die Antriebsachsen dienen zur Kraftübertragung von der Dampfmaschine zu anderen Maschinen. Die maximale Länge einer Antriebsachse beträgt 8 Blöcke. Über Getriebeboxen können auch größere Strecken überbrückt\\, sowie Abzweigungen und Richtungswechsel realisiert werden.\n".. + "\n".. + "\n".. + "\n", "Um Gegenstände (Items) von einer Verarbeitungsstation zur nächsten weiter zu transportieren\\, werden Schieber und Röhren verwendet. Siehe Plan.\n".. "\n".. "\n".. @@ -1487,6 +1493,7 @@ techage.manual_DE.aItemName = { "ta2_cylinder", "ta2_flywheel", "ta2_steampipe", + "ta2_driveaxle", "", "tube", "ta2_pusher", @@ -1670,6 +1677,7 @@ techage.manual_DE.aPlanTable = { "", "", "", + "", "itemtransport", "", "", diff --git a/techage/doc/manual_EN.lua b/techage/doc/manual_EN.lua index 2c6160e..9fba149 100644 --- a/techage/doc/manual_EN.lua +++ b/techage/doc/manual_EN.lua @@ -26,6 +26,7 @@ techage.manual_EN.aTitel = { "3,TA2 Cylinder", "3,TA2 Flywheel", "3,TA2 Steam Pipes", + "3,TA2 Drive Axle / TA2 Gearbox", "2,Push and sort items", "3,TechAge Tube", "3,TA2 Pusher", @@ -224,7 +225,7 @@ techage.manual_EN.aText = { " - Petroleum - is needed in TA3\n".. " - Bauxite - an aluminum ore that is needed in TA4 to produce aluminum\n".. "\n", - "Meridium is an alloy of steel and mesecons crystals. Meridium ingots can be made with the coal burner from steel andesecons crystals. Meridium glows in the dark. Tools made of Meridium also light up and are therefore very helpful in underground mining.\n".. + "Meridium is an alloy of steel and mesecons crystals. Meridium ingots can be made with the coal burner from steel and mesecons crystals. Meridium glows in the dark. Tools made of Meridium also light up and are therefore very helpful in underground mining.\n".. "\n".. "\n".. "\n", @@ -233,6 +234,7 @@ techage.manual_EN.aText = { "\n".. "\n", "Baborium is only extracted in underground mining. Baborium can only be found in stone at an altitude between -250 and -340 meters.\n".. + "Baborium can only be melted in the TA3 Industrial Furnace.\n".. "\n".. "\n".. "\n", @@ -326,7 +328,7 @@ techage.manual_EN.aText = { "\n".. "\n".. "\n", - "TA1 has its own metal alloy meridium. Meridium ingots can be made with the coal burner from steel and mesecons splinters. Meridium glows in the dark. Tools made of Meridium also light up and are therefore very helpful in underground mining.\n".. + "TA1 has its own metal alloy meridium. Meridium ingots can be made with the coal burner from steel and mesecons crystals. Meridium glows in the dark. Tools made of Meridium also light up and are therefore very helpful in underground mining.\n".. "\n".. "\n".. "\n", @@ -377,6 +379,10 @@ techage.manual_EN.aText = { "\n".. "\n".. "\n", + "The drive axles are used to transmit power from the steam engine to other machines. The maximum length of a drive axis is 8 blocks. With TA2 Gearboxes\\, larger distances can be bridged\\, and branches and changes of direction can be realized.\n".. + "\n".. + "\n".. + "\n", "In order to transport objects from one processing station to the next\\, pushers and tubes are used. See plan.\n".. "\n".. "\n".. @@ -1477,6 +1483,7 @@ techage.manual_EN.aItemName = { "ta2_cylinder", "ta2_flywheel", "ta2_steampipe", + "ta2_driveaxle", "", "tube", "ta2_pusher", @@ -1660,6 +1667,7 @@ techage.manual_EN.aPlanTable = { "", "", "", + "", "itemtransport", "", "", diff --git a/techage/furnace/recipes.lua b/techage/furnace/recipes.lua index 72bac4b..b951621 100644 --- a/techage/furnace/recipes.lua +++ b/techage/furnace/recipes.lua @@ -25,7 +25,6 @@ techage.furnace.register_recipe({ time = 8, }) - if techage.modified_recipes_enabled then techage.furnace.register_recipe({ output = "default:bronze_ingot 4", @@ -148,3 +147,24 @@ techage.furnace.register_recipe({ }, time = 4, }) + +if minetest.global_exists("moreores") then + + if techage.modified_recipes_enabled then + minetest.clear_craft({output = "moreores:mithril_ingot"}) + minetest.clear_craft({output = "moreores:silver_ingot"}) + end + + techage.furnace.register_recipe({ + output = 'moreores:silver_ingot', + recipe = {'moreores:silver_lump'}, + time = 2, + }) + + techage.furnace.register_recipe({ + output = 'moreores:mithril_ingot', + recipe = {'moreores:mithril_lump'}, + time = 5, + }) + +end diff --git a/techage/icta_controller/commands.lua b/techage/icta_controller/commands.lua index f1b096a..e140e4d 100644 --- a/techage/icta_controller/commands.lua +++ b/techage/icta_controller/commands.lua @@ -688,4 +688,3 @@ techage.icta_register_action("set_filter", { return send_single_string(environ, data.number, "filter", payload) end, }) - diff --git a/techage/icta_controller/controller.lua b/techage/icta_controller/controller.lua index 1a2ca3c..9b41145 100644 --- a/techage/icta_controller/controller.lua +++ b/techage/icta_controller/controller.lua @@ -451,8 +451,8 @@ minetest.register_node("techage:ta4_icta_controller", { return end - minetest.node_dig(pos, node, puncher, pointed_thing) techage.remove_node(pos) + minetest.node_dig(pos, node, puncher, pointed_thing) end, on_timer = on_timer, diff --git a/techage/icta_controller/display.lua b/techage/icta_controller/display.lua index 0c37533..04640c5 100644 --- a/techage/icta_controller/display.lua +++ b/techage/icta_controller/display.lua @@ -110,8 +110,8 @@ minetest.register_node("techage:ta4_display", { minetest.get_node_timer(pos):start(1) end, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) end, on_timer = on_timer, @@ -157,8 +157,8 @@ minetest.register_node("techage:ta4_displayXL", { minetest.get_node_timer(pos):start(2) end, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) end, on_timer = on_timer, diff --git a/techage/icta_controller/signaltower.lua b/techage/icta_controller/signaltower.lua index 5d51f05..5aeebc7 100644 --- a/techage/icta_controller/signaltower.lua +++ b/techage/icta_controller/signaltower.lua @@ -56,8 +56,8 @@ minetest.register_node("techage:ta4_signaltower", { end end, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) end, paramtype = "light", diff --git a/techage/init.lua b/techage/init.lua index 6a35d86..eac5e3a 100644 --- a/techage/init.lua +++ b/techage/init.lua @@ -13,7 +13,7 @@ techage = {} -- Version for compatibility checks, see readme.md/history -techage.version = 0.10 +techage.version = 0.12 if minetest.global_exists("tubelib") then minetest.log("error", "[techage] Techage can't be used together with the mod tubelib!") @@ -27,8 +27,8 @@ elseif minetest.global_exists("techpack") then elseif minetest.global_exists("tubelib2") and tubelib2.version < 1.9 then minetest.log("error", "[techage] Techage requires tubelib2 version 1.9 or newer!") return -elseif minetest.global_exists("minecart") and minecart.version < 1.05 then - minetest.log("error", "[techage] Techage requires minecart version 1.05 or newer!") +elseif minetest.global_exists("minecart") and minecart.version < 1.06 then + minetest.log("error", "[techage] Techage requires minecart version 1.06 or newer!") return elseif minetest.global_exists("lcdlib") and lcdlib.version < 1.0 then minetest.log("error", "[techage] Techage requires lcdlib version 1.0 or newer!") @@ -278,6 +278,7 @@ dofile(MP.."/lua_controller/sensorchest.lua") dofile(MP.."/lua_controller/terminal.lua") -- Items +dofile(MP.."/items/registered_nodes.lua") dofile(MP.."/items/barrel.lua") dofile(MP.."/items/baborium.lua") dofile(MP.."/items/usmium.lua") diff --git a/techage/iron_age/charcoalpile.lua b/techage/iron_age/charcoalpile.lua index ae68aaa..8354107 100644 --- a/techage/iron_age/charcoalpile.lua +++ b/techage/iron_age/charcoalpile.lua @@ -29,8 +29,7 @@ end local function num_dirt(pos) local pos1 = {x=pos.x-2, y=pos.y-1, z=pos.z-2} local pos2 = {x=pos.x+2, y=pos.y+3, z=pos.z+2} - local nodes = minetest.find_nodes_in_area(pos1, pos2, {"default:dirt", "default:dirt_with_grass", - "default:dirt_with_dry_grass", "default:dirt_with_snow", "techage:dirt_with_ash"}) + local nodes = minetest.find_nodes_in_area(pos1, pos2, techage.aAnyKindOfDirtBlocks) return #nodes end @@ -38,9 +37,18 @@ end local function make_dirt_with_dry_grass(pos) local pos1 = {x=pos.x-2, y=pos.y+3, z=pos.z-2} local pos2 = {x=pos.x+2, y=pos.y+3, z=pos.z+2} - for _,p in ipairs(minetest.find_nodes_in_area(pos1, pos2, "default:dirt_with_grass")) do + for _,p in ipairs(minetest.find_nodes_in_area(pos1, pos2, { + "default:dirt_with_grass", + "default:dirt_with_coniferous_litter", + "default:dirt_with_rainforest_litter", + })) do minetest.swap_node(p, {name = "default:dirt_with_dry_grass"}) end + if minetest.global_exists("ethereal") then + for _,p in ipairs(minetest.find_nodes_in_area(pos1, pos2, techage.aEtherealDirts)) do + minetest.swap_node(p, {name = "default:dirt_with_dry_grass"}) + end + end end -- replace pile bottom nodes diff --git a/techage/iron_age/gravelsieve.lua b/techage/iron_age/gravelsieve.lua index bfcaccc..db6e2db 100644 --- a/techage/iron_age/gravelsieve.lua +++ b/techage/iron_age/gravelsieve.lua @@ -35,9 +35,23 @@ local function push_items(pos, items) minetest.add_item({x=pos.x, y=pos.y-0.4, z=pos.z}, items) end +local function minecart_hopper_takeitem(pos, num) + for _, obj in pairs(minetest.get_objects_inside_radius({x=pos.x, y=pos.y-0.4, z=pos.z}, 0.2)) do + local entity = obj:get_luaentity() + if not obj:is_player() and entity and entity.name == "__builtin:item" then + obj:remove() + return ItemStack(entity.itemstring or "air") + end + end +end + +local function minecart_hopper_untakeitem(pos, in_dir, stack) + push_items(pos, stack) +end + local function keep_running(pos, elapsed) - local inv = M(pos):get_inventory() if swap_node(pos) then + local inv = M(pos):get_inventory() local src, dst if inv:contains_item("src", ItemStack("techage:basalt_gravel")) then @@ -53,6 +67,7 @@ local function keep_running(pos, elapsed) push_items(pos, dst) inv:remove_item("src", src) end + local inv = M(pos):get_inventory() return not inv:is_empty("src") end @@ -127,6 +142,9 @@ for idx = 0,3 do on_construct = idx == 3 and on_construct or nil, on_punch = idx == 3 and on_punch or nil, on_timer = keep_running, + + minecart_hopper_takeitem = minecart_hopper_takeitem, + minecart_hopper_untakeitem = minecart_hopper_untakeitem, paramtype = "light", sounds = default.node_sound_wood_defaults(), diff --git a/techage/iron_age/hopper.lua b/techage/iron_age/hopper.lua index 49db646..50f33b4 100644 --- a/techage/iron_age/hopper.lua +++ b/techage/iron_age/hopper.lua @@ -73,8 +73,5 @@ minecart.register_inventory( end, listname = "src", }, - take = { - listname = "src", - }, } ) diff --git a/techage/iron_age/recipes.lua b/techage/iron_age/recipes.lua index f721204..e2699f4 100644 --- a/techage/iron_age/recipes.lua +++ b/techage/iron_age/recipes.lua @@ -142,6 +142,9 @@ if techage.modified_recipes_enabled then minetest.clear_craft({output = "default:steel_ingot"}) minetest.clear_craft({output = "fire:flint_and_steel"}) minetest.clear_craft({output = "bucket:bucket_empty"}) + if minetest.global_exists("moreores") then + minetest.clear_craft({output = "moreores:silver_ingot"}) + end -- add again minetest.register_craft({ @@ -171,6 +174,23 @@ if techage.modified_recipes_enabled then time = 8, }) + techage.ironage_register_recipe({ + output = "default:tin_ingot 1", + recipe = {"default:tin_lump"}, + heat = 4, + time = 2, + }) + + if minetest.global_exists("moreores") then + techage.ironage_register_recipe({ + output = "moreores:silver_ingot 1", + recipe = {"moreores:silver_lump"}, + heat = 5, + time = 2, + }) + + end + minetest.register_craft({ output = "fire:flint_and_steel", recipe = { diff --git a/techage/items/baborium.lua b/techage/items/baborium.lua index e29dec2..55176ea 100644 --- a/techage/items/baborium.lua +++ b/techage/items/baborium.lua @@ -43,9 +43,8 @@ minetest.register_ore({ y_max = -250, }) -minetest.register_craft({ - type = 'cooking', +techage.furnace.register_recipe({ output = 'techage:baborium_ingot', - recipe = 'techage:baborium_lump', - cooktime = 5, + recipe = {'techage:baborium_lump'}, + time = 3, }) diff --git a/techage/items/registered_nodes.lua b/techage/items/registered_nodes.lua new file mode 100644 index 0000000..8f6d156 --- /dev/null +++ b/techage/items/registered_nodes.lua @@ -0,0 +1,39 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Collect data of registered nodes + +]]-- + + +techage.aEtherealDirts = { + "ethereal:fiery_dirt", + "ethereal:cold_dirt", + "ethereal:crystal_dirt", + "ethereal:gray_dirt", + "ethereal:mushroom_dirt", + "ethereal:prairie_dirt", + "ethereal:grove_dirt", + "ethereal:jungle_dirt", + "ethereal:bamboo_dirt", +} + +techage.aAnyKindOfDirtBlocks = {} + +minetest.register_on_mods_loaded(function() + for name, ndef in pairs(minetest.registered_nodes) do + if string.find(name, "dirt") and + ndef.drawtype == "normal" and + ndef.groups.crumbly and ndef.groups.crumbly > 0 then + techage.aAnyKindOfDirtBlocks[#techage.aAnyKindOfDirtBlocks + 1] = name + end + end +end) + diff --git a/techage/liquids/silo.lua b/techage/liquids/silo.lua index 1114565..b6fb63d 100644 --- a/techage/liquids/silo.lua +++ b/techage/liquids/silo.lua @@ -161,7 +161,7 @@ minetest.register_node("techage:ta3_silo", { end, after_dig_node = function(pos, oldnode, oldmetadata, digger) Pipe:after_dig_node(pos) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) end, liquid = tLiquid, networks = tNetworks, @@ -206,7 +206,7 @@ minetest.register_node("techage:ta4_silo", { end, after_dig_node = function(pos, oldnode, oldmetadata, digger) Pipe:after_dig_node(pos) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) end, liquid = tLiquid, networks = tNetworks, diff --git a/techage/liquids/tank.lua b/techage/liquids/tank.lua index d5dace8..9663653 100644 --- a/techage/liquids/tank.lua +++ b/techage/liquids/tank.lua @@ -103,7 +103,7 @@ minetest.register_node("techage:ta3_tank", { on_punch = liquid.on_punch, after_dig_node = function(pos, oldnode, oldmetadata, digger) Pipe:after_dig_node(pos) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) end, liquid = { capa = CAPACITY, @@ -161,7 +161,7 @@ minetest.register_node("techage:oiltank", { on_punch = liquid.on_punch, after_dig_node = function(pos, oldnode, oldmetadata, digger) Pipe:after_dig_node(pos) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) end, liquid = { capa = CAPACITY * 4, @@ -209,7 +209,7 @@ minetest.register_node("techage:ta4_tank", { on_punch = liquid.on_punch, after_dig_node = function(pos, oldnode, oldmetadata, digger) Pipe:after_dig_node(pos) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) end, liquid = { capa = CAPACITY * 2, diff --git a/techage/logic/button.lua b/techage/logic/button.lua index cb77c77..12e1053 100644 --- a/techage/logic/button.lua +++ b/techage/logic/button.lua @@ -140,7 +140,7 @@ local function techage_set_numbers(pos, numbers, player_name) end local function after_dig_node(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) end minetest.register_node("techage:ta3_button_off", { diff --git a/techage/logic/cart_detector.lua b/techage/logic/cart_detector.lua index fbc49ff..e4cc429 100644 --- a/techage/logic/cart_detector.lua +++ b/techage/logic/cart_detector.lua @@ -73,7 +73,7 @@ local function techage_set_numbers(pos, numbers, player_name) end local function after_dig_node(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) end minetest.register_node("techage:ta3_cartdetector_off", { diff --git a/techage/logic/collector.lua b/techage/logic/collector.lua index 4f3ace4..c0fa6ef 100644 --- a/techage/logic/collector.lua +++ b/techage/logic/collector.lua @@ -177,8 +177,8 @@ minetest.register_node("techage:ta4_collector", { on_timer = on_timer, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) end, paramtype = "light", diff --git a/techage/logic/detector.lua b/techage/logic/detector.lua index 8a2bc49..a3fd146 100644 --- a/techage/logic/detector.lua +++ b/techage/logic/detector.lua @@ -76,7 +76,7 @@ local function techage_set_numbers(pos, numbers, player_name) end local function after_dig_node(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end diff --git a/techage/logic/doorblock.lua b/techage/logic/doorblock.lua index 721d819..70108db 100644 --- a/techage/logic/doorblock.lua +++ b/techage/logic/doorblock.lua @@ -78,8 +78,8 @@ for idx,pgn in ipairs(tPgns) do end end, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) end, paramtype = "light", diff --git a/techage/logic/doorcontroller.lua b/techage/logic/doorcontroller.lua index 8e77d04..b670755 100644 --- a/techage/logic/doorcontroller.lua +++ b/techage/logic/doorcontroller.lua @@ -105,9 +105,9 @@ minetest.register_node("techage:ta3_doorcontroller", { return res end, - after_dig_node = function(pos) + after_dig_node = function(pos, oldnode, oldmetadata) swap_door_nodes(pos, false) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, diff --git a/techage/logic/gateblock.lua b/techage/logic/gateblock.lua index 447f536..f5ceb55 100644 --- a/techage/logic/gateblock.lua +++ b/techage/logic/gateblock.lua @@ -64,8 +64,8 @@ for idx,pgn in ipairs(tPgns) do end end, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) end, paramtype = "light", diff --git a/techage/logic/lua_logic.lua b/techage/logic/lua_logic.lua index 8b68be2..1fc9449 100644 --- a/techage/logic/lua_logic.lua +++ b/techage/logic/lua_logic.lua @@ -207,8 +207,8 @@ minetest.register_node("techage:ta3_logic", { return res end, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, diff --git a/techage/logic/node_detector.lua b/techage/logic/node_detector.lua index 50b491b..34cea52 100644 --- a/techage/logic/node_detector.lua +++ b/techage/logic/node_detector.lua @@ -135,7 +135,7 @@ minetest.register_node("techage:ta3_nodedetector_off", { end, after_dig_node = function(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, @@ -165,7 +165,7 @@ minetest.register_node("techage:ta3_nodedetector_on", { end, after_dig_node = function(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, diff --git a/techage/logic/player_detector.lua b/techage/logic/player_detector.lua index a4cfd94..ef4a120 100644 --- a/techage/logic/player_detector.lua +++ b/techage/logic/player_detector.lua @@ -133,7 +133,7 @@ minetest.register_node("techage:ta3_playerdetector_off", { end, after_dig_node = function(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, @@ -168,7 +168,7 @@ minetest.register_node("techage:ta3_playerdetector_on", { end, after_dig_node = function(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, @@ -224,7 +224,7 @@ minetest.register_node("techage:ta4_playerdetector_off", { end, after_dig_node = function(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, @@ -273,7 +273,7 @@ minetest.register_node("techage:ta4_playerdetector_on", { end, after_dig_node = function(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, diff --git a/techage/logic/repeater.lua b/techage/logic/repeater.lua index 9d0401a..4b24543 100644 --- a/techage/logic/repeater.lua +++ b/techage/logic/repeater.lua @@ -72,8 +72,8 @@ minetest.register_node("techage:ta3_repeater", { return res end, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, diff --git a/techage/logic/sequencer.lua b/techage/logic/sequencer.lua index 494cafc..45b8467 100644 --- a/techage/logic/sequencer.lua +++ b/techage/logic/sequencer.lua @@ -228,8 +228,8 @@ minetest.register_node("techage:ta3_sequencer", { end local nvm = techage.get_nvm(pos) if not nvm.running then - minetest.node_dig(pos, node, puncher, pointed_thing) techage.remove_node(pos) + minetest.node_dig(pos, node, puncher, pointed_thing) techage.del_mem(pos) end end, diff --git a/techage/logic/signallamp.lua b/techage/logic/signallamp.lua index fa4b10b..385960a 100644 --- a/techage/logic/signallamp.lua +++ b/techage/logic/signallamp.lua @@ -54,7 +54,7 @@ minetest.register_node("techage:signal_lamp_off", { on_rightclick = switch_on, after_dig_node = function(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) if COLORED then unifieddyes.after_dig_node(pos, oldnode, oldmetadata, digger) end @@ -97,7 +97,7 @@ minetest.register_node("techage:signal_lamp_on", { after_place_node = COLORED and unifieddyes.recolor_on_place or nil, after_dig_node = function(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) if COLORED then unifieddyes.after_dig_node(pos, oldnode, oldmetadata, digger) end diff --git a/techage/logic/terminal.lua b/techage/logic/terminal.lua index ed553ad..121d5c9 100644 --- a/techage/logic/terminal.lua +++ b/techage/logic/terminal.lua @@ -226,8 +226,8 @@ local function register_terminal(num, tiles, node_box, selection_box) end end, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) end, paramtype = "light", diff --git a/techage/logic/timer.lua b/techage/logic/timer.lua index bbdcfd2..0705c8b 100644 --- a/techage/logic/timer.lua +++ b/techage/logic/timer.lua @@ -175,8 +175,8 @@ minetest.register_node("techage:ta3_timer", { on_timer = check_rules, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, diff --git a/techage/lua_controller/controller.lua b/techage/lua_controller/controller.lua index a72261c..72f3acf 100644 --- a/techage/lua_controller/controller.lua +++ b/techage/lua_controller/controller.lua @@ -533,8 +533,8 @@ minetest.register_node("techage:ta4_lua_controller", { return end - minetest.node_dig(pos, node, puncher, pointed_thing) techage.remove_node(pos) + minetest.node_dig(pos, node, puncher, pointed_thing) end, on_timer = on_timer, diff --git a/techage/lua_controller/sensorchest.lua b/techage/lua_controller/sensorchest.lua index 7f46470..aa04ddc 100644 --- a/techage/lua_controller/sensorchest.lua +++ b/techage/lua_controller/sensorchest.lua @@ -89,7 +89,7 @@ local function can_dig(pos, player) end local function after_dig_node(pos, oldnode, oldmetadata, digger) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) end local function formspec1() diff --git a/techage/lua_controller/server.lua b/techage/lua_controller/server.lua index a23e394..f9ea5c3 100644 --- a/techage/lua_controller/server.lua +++ b/techage/lua_controller/server.lua @@ -86,8 +86,8 @@ minetest.register_node("techage:ta4_server", { return end techage.del_mem(pos) - minetest.node_dig(pos, node, puncher, pointed_thing) techage.remove_node(pos) + minetest.node_dig(pos, node, puncher, pointed_thing) end, on_timer = function(pos, elasped) diff --git a/techage/lua_controller/terminal.lua b/techage/lua_controller/terminal.lua index 4332c7d..5dfb0f5 100644 --- a/techage/lua_controller/terminal.lua +++ b/techage/lua_controller/terminal.lua @@ -173,8 +173,8 @@ minetest.register_node("techage:ta4_terminal", { end end, - after_dig_node = function(pos) - techage.remove_node(pos) + after_dig_node = function(pos, oldnode, oldmetadata) + techage.remove_node(pos, oldnode, oldmetadata) end, paramtype = "light", diff --git a/techage/manuals/QSG.md b/techage/manuals/QSG.md index 32afeb2..d5ff960 100644 --- a/techage/manuals/QSG.md +++ b/techage/manuals/QSG.md @@ -26,7 +26,7 @@ Since the levels build on each other, all ages have to be run through one after ## Iron Age (TA1) -1. Search and harvest cactus to make paper and craft the Techage Construction Board. This plan is the ingame manual for all four Techage phases +1. Search and harvest papyrus to make paper and craft the Techage Construction Board. This plan is the ingame manual for all four Techage phases 2. Cut trees and make wood out of them 3. Collect dirt for the charcoal burner to make charcoal 4. Go mining and seach for ores, or diff --git a/techage/manuals/manual_DE.md b/techage/manuals/manual_DE.md index 96187c0..cd50c95 100644 --- a/techage/manuals/manual_DE.md +++ b/techage/manuals/manual_DE.md @@ -65,6 +65,7 @@ Usmium kommt nur als Nuggets vor und kann nur beim Waschen von Kies mit der TA2/ ### Baborium Baborium wird nur im Untertagebau gewonnen. Baborium findet man nur in Stein in einer Höhe zwischen -250 und -340 Meter. +Baborium kann nur im TA3 Industrieofen geschmolzen werden. [baborium|image] diff --git a/techage/manuals/manual_EN.md b/techage/manuals/manual_EN.md index ead325d..f7848e3 100644 --- a/techage/manuals/manual_EN.md +++ b/techage/manuals/manual_EN.md @@ -50,7 +50,7 @@ Techage adds some new items to the game: ### Meridium -Meridium is an alloy of steel and mesecons crystals. Meridium ingots can be made with the coal burner from steel andesecons crystals. Meridium glows in the dark. Tools made of Meridium also light up and are therefore very helpful in underground mining. +Meridium is an alloy of steel and mesecons crystals. Meridium ingots can be made with the coal burner from steel and mesecons crystals. Meridium glows in the dark. Tools made of Meridium also light up and are therefore very helpful in underground mining. [meridium|image] @@ -65,6 +65,8 @@ Usmium only occurs as nuggets and can only be obtained by washing gravel with th ### Baborium Baborium is only extracted in underground mining. Baborium can only be found in stone at an altitude between -250 and -340 meters. +Baborium can only be melted in the TA3 Industrial Furnace. + [baborium|image] diff --git a/techage/manuals/manual_ta1_EN.md b/techage/manuals/manual_ta1_EN.md index fc1cbe5..b6179b5 100644 --- a/techage/manuals/manual_ta1_EN.md +++ b/techage/manuals/manual_ta1_EN.md @@ -98,6 +98,6 @@ Make sure that the boxes are "chest_locked", otherwise someone will steal the va ### Meridium -TA1 has its own metal alloy meridium. Meridium ingots can be made with the coal burner from steel and mesecons splinters. Meridium glows in the dark. Tools made of Meridium also light up and are therefore very helpful in underground mining. +TA1 has its own metal alloy meridium. Meridium ingots can be made with the coal burner from steel and mesecons crystals. Meridium glows in the dark. Tools made of Meridium also light up and are therefore very helpful in underground mining. [meridium|image] diff --git a/techage/manuals/manual_ta2_DE.md b/techage/manuals/manual_ta2_DE.md index 20ffe5c..6b718d9 100644 --- a/techage/manuals/manual_ta2_DE.md +++ b/techage/manuals/manual_ta2_DE.md @@ -64,6 +64,12 @@ Teil der Dampfmaschine. Der Boiler muss mit dem Zylinder über die Dampfleitunge [ta2_steampipe|image] +### TA2 Antriebsachsen / TA2 Drive Axle + +Die Antriebsachsen dienen zur Kraftübertragung von der Dampfmaschine zu anderen Maschinen. Die maximale Länge einer Antriebsachse beträgt 8 Blöcke. Über Getriebeboxen können auch größere Strecken überbrückt, sowie Abzweigungen und Richtungswechsel realisiert werden. + +[ta2_driveaxle|image] + ## Items schieben und sortieren diff --git a/techage/manuals/manual_ta2_EN.md b/techage/manuals/manual_ta2_EN.md index 87bcc6f..63d0db8 100644 --- a/techage/manuals/manual_ta2_EN.md +++ b/techage/manuals/manual_ta2_EN.md @@ -65,6 +65,12 @@ Part of the steam engine. The boiler must be connected to the cylinder via the s [ta2_steampipe|image] +### TA2 Drive Axle / TA2 Gearbox + +The drive axles are used to transmit power from the steam engine to other machines. The maximum length of a drive axis is 8 blocks. With TA2 Gearboxes, larger distances can be bridged, and branches and changes of direction can be realized. + +[ta2_driveaxle|image] + ## Push and sort items In order to transport objects from one processing station to the next, pushers and tubes are used. See plan. diff --git a/techage/manuals/ta4_lua_controller_EN.pdf b/techage/manuals/ta4_lua_controller_EN.pdf index 06a17c0..62ffe0b 100644 Binary files a/techage/manuals/ta4_lua_controller_EN.pdf and b/techage/manuals/ta4_lua_controller_EN.pdf differ diff --git a/techage/manuals/toc_DE.md b/techage/manuals/toc_DE.md index 077913b..e8c28f8 100644 --- a/techage/manuals/toc_DE.md +++ b/techage/manuals/toc_DE.md @@ -25,6 +25,7 @@ - [TA2 Zylinder /Cylinder](./manual_ta2_DE.md#ta2-zylinder-cylinder) - [TA2 Schwungrad / Flywheel](./manual_ta2_DE.md#ta2-schwungrad--flywheel) - [TA2 Dampfleitungen / Steam Pipe](./manual_ta2_DE.md#ta2-dampfleitungen--steam-pipe) + - [TA2 Antriebsachsen / TA2 Drive Axle](./manual_ta2_DE.md#ta2-antriebsachsen--ta2-drive-axle) - [Items schieben und sortieren](./manual_ta2_DE.md#items-schieben-und-sortieren) - [Röhren / TechAge Tube](./manual_ta2_DE.md#röhren--techage-tube) - [TA2 Schieber / Pusher](./manual_ta2_DE.md#ta2-schieber--pusher) diff --git a/techage/manuals/toc_EN.md b/techage/manuals/toc_EN.md index 71a0aab..4efd76f 100644 --- a/techage/manuals/toc_EN.md +++ b/techage/manuals/toc_EN.md @@ -25,6 +25,7 @@ - [TA2 Cylinder](./manual_ta2_EN.md#ta2-cylinder) - [TA2 Flywheel](./manual_ta2_EN.md#ta2-flywheel) - [TA2 Steam Pipes](./manual_ta2_EN.md#ta2-steam-pipes) + - [TA2 Drive Axle / TA2 Gearbox](./manual_ta2_EN.md#ta2-drive-axle--ta2-gearbox) - [Push and sort items](./manual_ta2_EN.md#push-and-sort-items) - [TechAge Tube](./manual_ta2_EN.md#techage-tube) - [TA2 Pusher](./manual_ta2_EN.md#ta2-pusher) diff --git a/techage/mod.conf b/techage/mod.conf index fc97803..7151add 100644 --- a/techage/mod.conf +++ b/techage/mod.conf @@ -1,4 +1,4 @@ name = techage depends = default,doors,tubelib2,basic_materials,bucket,stairs,screwdriver,minecart,lcdlib,safer_lua -optional_depends = unified_inventory,wielded_light,unifieddyes -description = Techage, go through 4 tech ages in search of wealth and power! \ No newline at end of file +optional_depends = unified_inventory,wielded_light,unifieddyes,moreores, ethereal +description = Techage, go through 4 tech ages in search of wealth and power! diff --git a/techage/solar/minicell.lua b/techage/solar/minicell.lua index 795ba9e..ec79066 100644 --- a/techage/solar/minicell.lua +++ b/techage/solar/minicell.lua @@ -72,9 +72,9 @@ local function after_place_node(pos) Cable:after_place_node(pos) end -local function after_dig_node(pos, oldnode) +local function after_dig_node(pos, oldnode, oldmetadata) Cable:after_dig_node(pos) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end diff --git a/techage/wind_turbine/rotor.lua b/techage/wind_turbine/rotor.lua index 43f0184..5be5909 100644 --- a/techage/wind_turbine/rotor.lua +++ b/techage/wind_turbine/rotor.lua @@ -171,14 +171,14 @@ local function after_place_node(pos, placer) Cable:after_place_node(pos) end -local function after_dig_node(pos, oldnode) +local function after_dig_node(pos, oldnode, oldmetadata) local hash = minetest.hash_node_position(pos) if Rotors[hash] and Rotors[hash]:get_luaentity() then Rotors[hash]:remove() end Rotors[hash] = nil Cable:after_dig_node(pos) - techage.remove_node(pos) + techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end diff --git a/unified_inventory/.luacheckrc b/unified_inventory/.luacheckrc index 709c485..9fb6a7c 100644 --- a/unified_inventory/.luacheckrc +++ b/unified_inventory/.luacheckrc @@ -12,6 +12,8 @@ read_globals = { "minetest", "vector", "ItemStack", "datastorage", + + "hb", } files["callbacks.lua"].ignore = { "player", "draw_lite_mode" } diff --git a/unified_inventory/item_names.lua b/unified_inventory/item_names.lua index 111c9ee..a1368f8 100644 --- a/unified_inventory/item_names.lua +++ b/unified_inventory/item_names.lua @@ -2,24 +2,24 @@ local item_names = {} -- [player_name] = { hud, dtime, itemname } local dlimit = 3 -- HUD element will be hidden after this many seconds -local air_hud_mod = minetest.get_modpath("4air") -local hud_mod = minetest.get_modpath("hud") local hudbars_mod = minetest.get_modpath("hudbars") local function set_hud(player) local player_name = player:get_player_name() - local off = {x=0, y=-70} - if air_hud_mod or hud_mod then - off.y = off.y - 20 - elseif hudbars_mod then - off.y = off.y + 13 + local off = {x=0, y=-65} + if hudbars_mod then + -- Assume no alignment (2 per line) + off.y = off.y - math.ceil(hb.hudbars_count / 2) * 25 + else + off.y = off.y - 25 end + item_names[player_name] = { hud = player:hud_add({ hud_elem_type = "text", position = {x=0.5, y=1}, offset = off, - alignment = {x=0, y=0}, + alignment = {x=0, y=-1}, number = 0xFFFFFF, text = "", }),