From d2d5e17c96b7424c56c7528d91d0b1c1a61f6147 Mon Sep 17 00:00:00 2001 From: Joachim Stolberg Date: Sat, 24 Jun 2017 23:03:24 +0200 Subject: [PATCH] tubes seam to work --- door.lua | 2 +- init.lua | 352 +--------------------------------------------------- order.lua | 58 +++++---- station.lua | 213 +++++++++++++++++++------------ test.lua | 1 + tubes.lua | 338 +++++++++++++++++++++++++------------------------ utils.lua | 177 ++++++++++++++++++++------ 7 files changed, 496 insertions(+), 645 deletions(-) diff --git a/door.lua b/door.lua index 5d8f063..0729c72 100644 --- a/door.lua +++ b/door.lua @@ -86,7 +86,7 @@ minetest.register_node("hyperloop:doorBottom", { }) minetest.register_node("hyperloop:doorframe", { - description = "Hyperloop Doorframe", + description = "Hyperloop Pod Doorframe", tiles = { -- up, down, right, left, back, front "hyperloop_skin_door.png^[transformR90]", diff --git a/init.lua b/init.lua index b7d7780..1b8e2a5 100755 --- a/init.lua +++ b/init.lua @@ -17,355 +17,15 @@ hyperloop = { - ringList = {}, + tAllStations = {}, + order = {}, } --- max teleport distance --- Intllib -local S - -if minetest.get_modpath("intllib") then - S = intllib.Getter() -else - S = function(s, a, ...) a = {a, ...} - return s:gsub("@(%d+)", function(n) - return a[tonumber(n)] - end) - end - -end - - -local dist = tonumber(minetest.setting_get("map_generation_limit") or 31000) - - dofile(minetest.get_modpath("hyperloop") .. "/utils.lua") -dofile(minetest.get_modpath("hyperloop") .. "/door.lua") dofile(minetest.get_modpath("hyperloop") .. "/tubes.lua") - ----------------------------------------------------------------------------------------------------- -local function enter_display(pos, text) - -- Use LCD from digilines. TODO: Own display - local node = minetest.get_node(pos) - local spec = digilines.getspec(node) - if spec then - -- Effector actions --> Receive - if spec.effector then - spec.effector.action(pos, node, "lcd", text) - end - end -end - ----------------------------------------------------------------------------------------------------- -local function check_coordinates(str) - -- obsolete? - if not str or str == "" then - return nil - end - - -- get coords from string - local x, y, z, desc = string.match(str, "^(-?%d+),(-?%d+),(-?%d+),?(.*)$") - - -- check coords - if x == nil or string.len(x) > 6 - or y == nil or string.len(y) > 6 - or z == nil or string.len(z) > 6 then - return nil - end - - -- convert string coords to numbers - x = tonumber(x) - y = tonumber(y) - z = tonumber(z) - - -- are coords in map range ? - if x > dist or x < -dist - or y > dist or y < -dist - or z > dist or z < -dist then - return nil - end - - -- return ok coords - return {x = x, y = y, z = z, desc = desc} -end - ----------------------------------------------------------------------------------------------------- --- seat_pos: position of the seat --- facedir: direction to the display --- cmnd: "close", "open", or "animate" -local function door_command(seat_pos, facedir, cmnd) - -- one step forward - local lcd_pos = vector.add(seat_pos, facedir2dir(facedir)) - -- one step left - local door_pos1 = vector.add(lcd_pos, facedir2dir(facedir + 1)) - -- one step up - local door_pos2 = vector.add(door_pos1, {x=0, y=1, z=0}) - - local node1 = minetest.get_node(door_pos1) - local node2 = minetest.get_node(door_pos2) - - -- switch from the radian following facedir to the silly original one - local tbl = {[0]=0, [1]=3, [2]=2, [3]=1} - facedir = (facedir + 3) % 4 -- first turn left - facedir = tbl[facedir] - - if cmnd == "open" then - node1.name = "air" - minetest.swap_node(door_pos1, node1) - node2.name = "air" - minetest.swap_node(door_pos2, node2) - elseif cmnd == "close" then - node1.name = "hyperloop:doorBottom" - node1.param2 = facedir - minetest.swap_node(door_pos1, node1) - node2.name = "hyperloop:doorTopPassive" - node2.param2 = facedir - minetest.swap_node(door_pos2, node2) - elseif cmnd == "animate" then - node2.name = "hyperloop:doorTopActive" - node2.param2 = facedir - minetest.swap_node(door_pos2, node2) - end -end - ----------------------------------------------------------------------------------------------------- -local function on_open_door(pos, facedir) - -- open the door and play sound - local meta = minetest.get_meta(pos) - meta:set_int("arrival_time", 0) -- finished - - -- open the door - minetest.sound_play("door", { - pos = pos, - gain = 0.5, - max_hear_distance = 10, - }) - door_command(pos, facedir, "open") - - -- prepare dislay for the next trip - local text = "We will start | in a few | seconds" - enter_display(lcd_pos, text) -end - ----------------------------------------------------------------------------------------------------- -local function on_arrival(player, src_pos, dst_pos, snd, radiant) - -- open the door an the departure station - local meta = minetest.get_meta(src_pos) - local facedir = meta:get_int("facedir") - door_command(src_pos, facedir, "open") - - -- get coords from arrival station - meta = minetest.get_meta(dst_pos) - facedir = meta:get_int("facedir") - --print("on_arrival "..dump(dst_pos))---------------------------------------------- - - -- close the door at arrival station - door_command(dst_pos, facedir, "close") - - -- move player to the arrival station - player:setpos(dst_pos) - -- rotate player to look in correct arrival direction - -- calculate the look correction - local offs = radiant - player:get_look_horizontal() - local yaw = facedir2rad(facedir) + offs - player:set_look_yaw(yaw) - - -- play arrival sound - minetest.sound_stop(snd) - minetest.sound_play("down", { - pos = dst_pos, - gain = 1.0, - max_hear_distance = 10 - }) - -- activate display - local lcd_pos = vector.add(dst_pos, facedir2dir(facedir)) - lcd_pos.y = lcd_pos.y + 1 - --print("LCD "..dump(pos)..dump(lcd_pos)) - local text = "Wellcome in | | Hauptstadt" - enter_display(lcd_pos, text) - - minetest.after(6.0, on_open_door, dst_pos, facedir) -end - ----------------------------------------------------------------------------------------------------- -local function on_travel(src_pos, facedir, player, dst_pos, radiant) - -- play sound and switch door state - -- radiant is the player look direction at departure - local snd = minetest.sound_play("normal", { - pos = src_pos, - gain = 1.0, - max_hear_distance = 1, - loop = true, - }) - door_command(src_pos, facedir, "animate") - minetest.after(6.0, on_arrival, player, src_pos, dst_pos, snd, radiant) -end - ----------------------------------------------------------------------------------------------------- -local function display_timer(pos, elapsed) - -- update display with trip data - local meta = minetest.get_meta(pos) - local atime = meta:get_int("arrival_time") - 1 - meta:set_int("arrival_time", atime) - local lcd_pos = minetest.string_to_pos(meta:get_string("lcd_pos")) - local text = meta:get_string("lcd_text") - if atime > 0 then - enter_display(lcd_pos, text..atime.." sec") - return true - else - enter_display(lcd_pos, "We will start | in a view | minutes..") - return false - end -end - - ----------------------------------------------------------------------------------------------------- -local function on_start_travel(pos, node, clicker) - -- place the player, close the door, activate display - local meta = minetest.get_meta(pos) - local facedir = meta:get_int("facedir") - if meta:get_int("arrival_time") ~= 0 then - return - end - local target_coords = { - x = meta:get_int("x"), - y = meta:get_int("y"), - z = meta:get_int("z") - } - - minetest.sound_play("up", { - pos = pos, - gain = 1.0, - max_hear_distance = 10 - }) - -- place player on the seat - clicker:setpos(pos) - -- rotate player to look in move direction - clicker:set_look_horizontal(facedir2rad(facedir)) - - -- activate display - local lcd_pos = vector.add(pos, facedir2dir(facedir)) - lcd_pos.y = lcd_pos.y + 1 - --print("LCD "..dump(pos)..dump(lcd_pos)) - local text = "Next stop: | Hauptstadt | Dist: 2.2km | Arrival in: | " - local atime = 15 - enter_display(lcd_pos, text..atime.." sec") - - -- store some data - meta:set_int("arrival_time", atime) - meta:set_string("lcd_pos", minetest.pos_to_string(lcd_pos)) - meta:set_string("lcd_text", text) - meta:set_string("lcd_text", text) - minetest.get_node_timer(pos):start(1.0) - - --print("on_rightclick "..dump(pos))---------------------------------------------- - - -- close the door - minetest.sound_play("door", { - pos = pos, - gain = 0.5, - max_hear_distance = 10, - }) - door_command(pos, facedir, "close") - - minetest.after(4.9, on_travel, pos, facedir, clicker, target_coords, facedir2rad(facedir)) -end - --- Hyperloop Seat -minetest.register_node("hyperloop:seat", { - tiles = { - "seat-top.png", - "seat-side.png", - "seat-side.png", - "seat-side.png", - "seat-side.png", - "seat-side.png", - }, - drawtype = "nodebox", - paramtype2 = "facedir", - is_ground_content = false, - walkable = false, - --description = S("Hyperloop Pad (place and right-click to enchant location)"), - groups = {snappy = 3}, - node_box = { - type = "fixed", - fixed = { - { -6/16, -8/16, -8/16, 6/16, -2/16, 5/16}, - { -8/16, -8/16, -8/16, -6/16, 4/16, 8/16}, - { 6/16, -8/16, -8/16, 8/16, 4/16, 8/16}, - { -6/16, -8/16, 4/16, 6/16, 6/16, 8/16}, - }, - }, - selection_box = { - type = "fixed", - fixed = { -8/16, -8/16, -8/16, 8/16, -2/16, 8/16 }, - }, - - on_timer = display_timer, - - on_construct = function(pos) - - local meta = minetest.get_meta(pos) - - -- text entry formspec - meta:set_string("formspec", "field[text;" .. S("Enter teleport coords (e.g. 200,20,-200,Home)") .. ";${text}]") - meta:set_string("infotext", S("Right-click to enchant teleport location")) - meta:set_string("text", pos.x .. "," .. pos.y .. "," .. pos.z) - meta:set_int("arrival_time", 0) - - -- set default coords - meta:set_int("x", pos.x) - meta:set_int("y", pos.y) - meta:set_int("z", pos.z) - end, - - after_place_node = function(pos, placer) - local meta = minetest.get_meta(pos) - local yaw = placer:get_look_horizontal() - -- facedir according to radiant - local facedir = rad2facedir(yaw) - -- do a 180 degree correction - meta:set_int("facedir", (facedir + 2) % 4) - print("on_construct "..dump(pos))---------------------------------------------- - end, - - -- once entered, check coords, if ok then return potion - on_receive_fields = function(pos, formname, fields, sender) - - local name = sender:get_player_name() - - if minetest.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) - return - end - - local coords = check_coordinates(fields.text) - - if coords then - - local meta = minetest.get_meta(pos) - print("on_receive_fields "..dump(pos))---------------------------------------------- - - meta:set_int("x", coords.x) - meta:set_int("y", coords.y) - meta:set_int("z", coords.z) - meta:set_string("text", fields.text) - - if coords.desc and coords.desc ~= "" then - - meta:set_string("infotext", S("Teleport to @1", coords.desc)) - else - meta:set_string("infotext", S("Pad Active (@1,@2,@3)", - coords.x, coords.y, coords.z)) - end - -- delete formspec so that right-click will work - meta:set_string("formspec", nil) - else - minetest.chat_send_player(name, S("Teleport Pad coordinates failed!")) - end - end, - - on_rightclick = on_start_travel, -}) +dofile(minetest.get_modpath("hyperloop") .. "/order.lua") +dofile(minetest.get_modpath("hyperloop") .. "/station.lua") +dofile(minetest.get_modpath("hyperloop") .. "/door.lua") +dofile(minetest.get_modpath("hyperloop") .. "/seat.lua") print ("[MOD] Hyperloop loaded") diff --git a/order.lua b/order.lua index 83bb48a..d3b9377 100644 --- a/order.lua +++ b/order.lua @@ -20,23 +20,22 @@ local function final_formspec(name) if stations == nil then return nil end - - local tRes = {"size[10,9]label[3,0;Wähle dein Ziel / Select your destination]"} + table.sort(stations) + local tRes = {"size[10,9]label[2,0; Abfahrt ".. name ..": Wähle dein Ziel\nDeparture ".. name .. ": Select your destination]"} for idx,s in ipairs(stations) do if idx < 9 then pos1 = "0,"..idx - pos2 = "3,"..idx + pos2 = "1,"..idx else - pos1 = "6,"..(idx-8) - pos2 = "9,"..(idx-8) + pos1 = "5,"..(idx-8) + pos2 = "6,"..(idx-8) end - tRes[#tRes + 1] = "label["..pos1..".2;"..s.."]" - tRes[#tRes + 1] = "button_exit["..pos2..";1,1;h;X]" + tRes[#tRes + 1] = "button_exit["..pos1..";1,1;button;"..idx.."]" + tRes[#tRes + 1] = "label["..pos2..".2;"..s.."]" end return table.concat(tRes) end - minetest.register_node("hyperloop:order", { description = "Hyperloop Order Machine", tiles = { @@ -61,35 +60,44 @@ minetest.register_node("hyperloop:order", { on_receive_fields = function(pos, formname, fields, player) local meta = minetest.get_meta(pos) if fields.name ~= nil then - local station_name = fields.name - print(station_name) - meta:set_string("station_name", station_name) - local s = final_formspec(station_name) - if s == nil then - minetest.chat_send_player(player:get_player_name(), "Error: Invalid station name!") - else - meta:set_string("formspec", s) - if hyperloop.tAllStations ~= nil and hyperloop.tAllStations[station_name] ~= nil then - local tmp = hyperloop.tAllStations[station_name]["pos"] - hyperloop.tAllStations[station_name]["order"] = pos - meta:set_string("station_pos", tmp) + if hyperloop.tAllStations ~= nil and hyperloop.tAllStations[fields.name] ~= nil then + local station_pos = minetest.string_to_pos(hyperloop.tAllStations[fields.name].pos) + if hyperloop.distance(pos, station_pos) > 30 then + minetest.chat_send_player(player:get_player_name(), "Error: station too far away!") + return end + hyperloop.tAllStations[fields.name]["automat_pos"] = pos + meta:set_string("station_name", fields.name) + meta:set_string("infotext", "Station: "..fields.name) + meta:set_string("formspec", final_formspec(fields.name)) + else + minetest.chat_send_player(player:get_player_name(), "Error: Invalid station name!") end - else + elseif fields.button ~= nil then + local station_name = meta:get_string("station_name") + local stations = hyperloop.get_stations(table.copy(hyperloop.tAllStations), station_name, {}) + table.sort(stations) + -- place order + local idx = tonumber(fields.button) + print(station_name .. ":" .. stations[idx]) + hyperloop.order[station_name] = stations[idx] end end, on_destruct = function(pos) local meta = minetest.get_meta(pos) local station_name = meta:get_string("station_name") - hyperloop.tAllStations[station_name]["order"] = nil + if hyperloop.tAllStations ~= nil and hyperloop.tAllStations[station_name.name] ~= nil + and hyperloop.tAllStations[station_name.name]["automat_pos"] ~= nil then + hyperloop.tAllStations[station_name]["automat_pos"] = nil + end end, - + on_punch = function(pos) local meta = minetest.get_meta(pos) local station_name = meta:get_string("station_name") - local s = final_formspec(station_name) - meta:set_string("formspec", s) + meta:set_string("infotext", "Station: "..station_name) + meta:set_string("formspec", final_formspec(station_name)) end, paramtype2 = "facedir", diff --git a/station.lua b/station.lua index 168d958..6ab846c 100644 --- a/station.lua +++ b/station.lua @@ -1,84 +1,145 @@ --- We need: --- * tube_power0, can be placed and docked by a tube --- * tube_power1, can be docked by a second tube --- * tube_power2, can't be docked by a third tube -for idx = 0,2 do - local img - if idx < 2 then - img = "hyperloop_power_tube_green.png" - else - img = "hyperloop_power_tube_red.png" - end - minetest.register_node("hyperloop:tube_power"..idx, { - description = "Hyperloop Power Tube", - tiles = { - { - name = img, - animation = { - type = "vertical_frames", - aspect_w = 32, - aspect_h = 32, - length = 2.0, - }, - }, - }, +--[[ - after_place_node = function(pos, placer, itemstack, pointed_thing) - local nodes = scan_tube_neighbours(pos, true, {"hyperloop:tube_head", "hyperloop:tube_power1"}) - -- power node can't be placed nearby another power node - for _,node in ipairs(nodes) do - if node.name == "hyperloop:tube_power0" or node.name == "hyperloop:tube_power1" then - -- remove node again - minetest.chat_send_player(placer:get_player_name(), "Power Tube block can't be placed here.") - minetest.remove_node(pos) - return - end - end + Hyperloop Mod + ============= - local meta = minetest.get_meta(pos) - local station_name = meta:get_string("station") or "" - local ring_addr - if #nodes == 0 then -- are we the one and only? - --print("start ring")---------------------- - -- a new ring starts here - ring_addr = determine_ring_addr(pos) - else - --print("degrade to tubes")---------------------- - -- degrade neighbor nodes - swap_to_tube(pos, placer, nodes) - ring_addr = meta:get_string("ring_addr") + Copyright (C) 2017 Joachim Stolberg - -- already connected with two tube nodes? - if #nodes == 2 then - --print("switch to tube_power1")---------------------- - -- tube_power1 cant be docked by a third tube - local node = minetest.get_node(pos) - node.name = "hyperloop:tube_power1" - minetest.swap_node(pos, node) - end - end - meta:set_string("infotext", "Power Tube block "..idx..". ring at: "..ring_addr.." Station Name: "..station_name) - -- store ring_addr in ring list - if hyperloop.ringList[ring_addr] ~= nil then - table.insert(hyperloop.ringList[ring_addr], pos) - else - hyperloop.ringList[ring_addr] = {pos} - end - --print("store ring_addr in ring list")------------------------------------- - --hyperloop.dbg_ringlist()------------------------------------------ - end, + LGPLv2.1+ + See LICENSE.txt for more information - on_destruct = function(pos) - local nodes = scan_tube_neighbours(pos, true, {"hyperloop:tube"}) - -- upgrade neighbor nodes - swap_to_tube_head(pos, nodes) - end, + History: + see init.lua - paramtype2 = "facedir", - groups = {cracky=2, not_in_creative_inventory=idx}, - is_ground_content = false, - drop = "hyperloop:tube_power0", - }) +]]-- + +local function store_routes(pos) + local meta = minetest.get_meta(pos) + local station_name = meta:get_string("station_name") + print("station_name="..station_name) + if station_name ~= nil and station_name ~= "" then + local res, nodes = hyperloop.scan_neighbours(pos) + -- generate a list with all tube heads + local tRoutes = {} + for _,node in ipairs(nodes) do + print(node.name) + if node.name == "hyperloop:tube1" then + local meta = minetest.get_meta(node.pos) + local route = {meta:get_string("local"), meta:get_string("remote")} + --print(dump(route)) + table.insert(tRoutes, route) + end + end + -- store list + local spos = minetest.pos_to_string(pos) + hyperloop.tAllStations[station_name] = {pos=spos, routes=tRoutes} + end +end + +local function punch_all_stations() + for _, item in pairs(hyperloop.tAllStations) do + minetest.punch_node(minetest.string_to_pos(item.pos)) + end end +minetest.register_node("hyperloop:junction", { + description = "Hyperloop Junction Block", + tiles = {"hyperloop_station.png"}, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta(pos) + local formspec = "size[5,4]".. + "label[0,0;Please insert station name]" .. + "field[1,1.5;3,1;name;Name;MyTown]" .. + "button_exit[1,2;2,1;exit;Save]" + meta:set_string("formspec", formspec) + end, + + on_receive_fields = function(pos, formname, fields, player) + if fields.name == nil then + return + end + local station_name = string.trim(fields.name) + if station_name == "" then + return + end + -- check if station already available + local spos = minetest.pos_to_string(pos) + if hyperloop.tAllStations[station_name] ~= nil and hyperloop.tAllStations[station_name]["pos"] ~= spos then + minetest.chat_send_player(player:get_player_name(), "Error: Station name already assigned!") + return + end + local meta = minetest.get_meta(pos) + meta:set_string("formspec", nil) + meta:set_string("station_name", station_name) + meta:set_string("infotext", "Station '"..station_name.."'") + store_routes(pos) + -- update routes in station list + --punch_all_stations() --???????????????????????????????????????????? + end, + + on_punch = function(pos) + print("Junction punched") + store_routes(pos) + end, + + on_destruct = function(pos) + -- delete station data + local meta = minetest.get_meta(pos) + local station_name = meta:get_string("station_name") + if hyperloop.tAllStations[station_name] ~= nil then + hyperloop.tAllStations[station_name] = nil + end + -- update routes in station list + --punch_all_stations() + end, + + paramtype2 = "facedir", + groups = {cracky=2}, + is_ground_content = false, + }) + + +-- to build the pod +minetest.register_node("hyperloop:pod_wall", { + description = "Hyperloop Pod Wall", + tiles = { + -- up, down, right, left, back, front + "hyperloop_skin.png^[transformR90]", + "hyperloop_skin.png^[transformR90]", + "hyperloop_skin.png", + "hyperloop_skin.png", + "hyperloop_skin.png", + "hyperloop_skin.png", + }, + paramtype2 = "facedir", + groups = {cracky=1}, + is_ground_content = false, + }) + + +local function book_on_use(itemstack, user) + local player_name = user:get_player_name() + local pos = user:get_pos() + local sStationList = hyperloop.get_stations_as_string(pos) + local formspec = "size[8,8]" .. default.gui_bg .. + default.gui_bg_img .. + "textarea[0.5,0.5;7.5,8;text;Station List:;" .. + sStationList .. "]" .. + "button_exit[2.5,7.5;3,1;close;Close]" + + minetest.show_formspec(player_name, "default:book", formspec) + return itemstack +end + +-- Tool for tube workers to find the next station +minetest.register_node(":hyperloop:station_map", { + description = "Hyperloop Station Map", + inventory_image = "hyperloop_stations_book.png", + wield_image = "hyperloop_stations_book.png", + groups = {cracky=1, book=1}, + on_use = book_on_use, + on_place = book_on_use, + }) + diff --git a/test.lua b/test.lua index 059f2ba..92e9b90 100644 --- a/test.lua +++ b/test.lua @@ -165,6 +165,7 @@ res = hyperloop.get_stations(table.copy(hyperloop.tAllStations), "Düsseldorf", local function final_formspec(name) local stations = hyperloop.get_stations(table.copy(hyperloop.tAllStations), name, {}) + table.sort(stations) local tRes = {"size[10,9]label[3,0;Wähle dein Ziel / Select your destination]"} for idx,s in ipairs(stations) do if idx < 9 then diff --git a/tubes.lua b/tubes.lua index b333c63..ca3c036 100644 --- a/tubes.lua +++ b/tubes.lua @@ -13,196 +13,212 @@ ]]-- -local function scan_neighbours(pos) - -- Scan all 8 neighbor positions for tube nodes. - -- Return: - -- 0, nodes - no node available - -- 1, nodes - one tube1 available - -- 3, nodes - two tube1 available - -- 4, nodes - one tube2 available - -- 5, nodes - invalid position - local nodes = {} - local node, npos, idx - local res = 0 - for _,dir in ipairs(hyperloop.NeighborPos) do - npos = vector.add(pos, dir) - node = minetest.get_node(npos) - if string.find(node.name, "hyperloop:tube") then - node.pos = npos - table.insert(nodes, node) - idx = string.byte(node.name, -1) - 48 - if idx == 0 then -- starter tube node? - idx = 1 - elseif idx == 2 then -- normal tube node? - return 4, nodes - end - res = res * 2 + idx - if res > 3 then - return 5, nodes - end - end - end - return res, nodes +-- Block arrangement: [0] [1][2][1] [S][1][2][2][1][S] +-- Scan all 8 neighbor positions for tube nodes. +-- Return: +-- 0, nodes - no node available +-- 1, nodes - one tube1 available +-- 3, nodes - two tube1 available +-- 4, nodes - one tube2 available +-- 5, nodes - invalid position +function hyperloop.scan_neighbours(pos) + local nodes = {} + local node, npos, idx + local res = 0 + for _,dir in ipairs(hyperloop.NeighborPos) do + npos = vector.add(pos, dir) + node = minetest.get_node(npos) + if string.find(node.name, "hyperloop:tube") then + node.pos = npos + table.insert(nodes, node) + idx = string.byte(node.name, -1) - 48 + if idx == 0 then -- starter tube node? + idx = 1 + elseif idx == 2 then -- normal tube node? + return 4, nodes + end + res = res * 2 + idx + end + end + if res > 3 then + res = 5 + end + return res, nodes end +-- Degrade one node. +-- Needed when a new node is placed nearby. local function degrade_tupe_node(node) - print("degrade_tupe_node"..node.name) - if node.name == "hyperloop:tube0" then - node.name = "hyperloop:tube1" - elseif node.name == "hyperloop:tube1" then - node.name = "hyperloop:tube2" - node.diggable = false - else - return - end - minetest.swap_node(node.pos, node) + if node.name == "hyperloop:tube0" then + node.name = "hyperloop:tube1" + elseif node.name == "hyperloop:tube1" then + node.name = "hyperloop:tube2" + node.diggable = false + else + return + end + minetest.swap_node(node.pos, node) end +-- Upgrade one node. +-- Needed when a tube node is digged. local function upgrade_node(pos, node) - -- Upgrade one node. - -- Is needed when a tube node is digged. - local meta_local = minetest.get_meta(pos) - local meta_head = minetest.get_meta(node.pos) - meta_head:set_string("other", meta_local:get_string("other")) - meta_head:set_string("me", minetest.pos_to_string(node.pos)) - node.diggable = true - if node.name == "hyperloop:tube2" then -- 2 connections? - node.name = "hyperloop:tube1" - elseif node.name == "hyperloop:tube1" then -- 1 connection? - node.name = "hyperloop:tube0" - end - minetest.swap_node(node.pos, node) + local meta_local = minetest.get_meta(pos) + local meta_head = minetest.get_meta(node.pos) + meta_head:set_string("other", meta_local:get_string("other")) + meta_head:set_string("me", minetest.pos_to_string(node.pos)) + node.diggable = true + if node.name == "hyperloop:tube2" then -- 2 connections? + node.name = "hyperloop:tube1" + elseif node.name == "hyperloop:tube1" then -- 1 connection? + node.name = "hyperloop:tube0" + end + minetest.swap_node(node.pos, node) end +-- Place a node without neighbours local function starter_node(node) - local meta = minetest.get_meta(node.pos) - meta:set_string("local", minetest.pos_to_string(node.pos)) - meta:set_string("remote", minetest.pos_to_string(node.pos)) - -- upgrade self to starter node - node.name = "hyperloop:tube0" - minetest.swap_node(node.pos, node) + local meta = minetest.get_meta(node.pos) + meta:set_string("local", minetest.pos_to_string(node.pos)) + meta:set_string("remote", minetest.pos_to_string(node.pos)) + -- upgrade self to starter node + node.name = "hyperloop:tube0" + minetest.swap_node(node.pos, node) end +-- Place a node with one neighbor local function head_node(node, node1) - local meta_local = minetest.get_meta(node.pos) - local meta_head = minetest.get_meta(node1.pos) - -- set local data - meta_local:set_string("local", minetest.pos_to_string(node.pos)) - meta_local:set_string("remote", meta_head:get_string("remote")) - -- set remote data - local rpos = minetest.string_to_pos(meta_head:get_string("remote")) - local rmeta = minetest.get_meta(rpos) - rmeta:set_string("remote", minetest.pos_to_string(node.pos)) - -- upgrade self - node.name = "hyperloop:tube1" - minetest.swap_node(node.pos, node) - -- degrade old head - degrade_tupe_node(node1) + local meta_local = minetest.get_meta(node.pos) + local meta_head = minetest.get_meta(node1.pos) + -- set local data + meta_local:set_string("local", minetest.pos_to_string(node.pos)) + meta_local:set_string("remote", meta_head:get_string("remote")) + -- set remote data + local rpos = minetest.string_to_pos(meta_head:get_string("remote")) + local rmeta = minetest.get_meta(rpos) + rmeta:set_string("remote", minetest.pos_to_string(node.pos)) + print("rmeta:get_string(remote) = " .. rmeta:get_string("remote")) + -- punch remote node + minetest.punch_node(rpos) + -- upgrade self + node.name = "hyperloop:tube1" + minetest.swap_node(node.pos, node) + -- degrade old head + degrade_tupe_node(node1) end local function link_node(node, node1, node2) - -- determine the meta data from both remote heads - local meta_head1 = minetest.get_meta(node1.pos) - local meta_head2 = minetest.get_meta(node2.pos) - local pos1 = minetest.string_to_pos(meta_head1:get_string("remote")) - local pos2 = minetest.string_to_pos(meta_head2:get_string("remote")) - local meta_rmt1 = minetest.get_meta(pos1) - local meta_rmt2 = minetest.get_meta(pos2) - -- exchange position data - meta_rmt2:set_string("remote", meta_rmt1:get_string("local")) - meta_rmt1:set_string("remote", meta_rmt2:get_string("local")) - -- set to tube2 - node.name = "hyperloop:tube2" - node.diggable = true - minetest.swap_node(node.pos, node) - -- degrade both nodes - degrade_tupe_node(node1) - degrade_tupe_node(node2) + -- determine the meta data from both remote heads + local meta_head1 = minetest.get_meta(node1.pos) + local meta_head2 = minetest.get_meta(node2.pos) + local pos1 = minetest.string_to_pos(meta_head1:get_string("remote")) + local pos2 = minetest.string_to_pos(meta_head2:get_string("remote")) + local meta_rmt1 = minetest.get_meta(pos1) + local meta_rmt2 = minetest.get_meta(pos2) + -- exchange position data + meta_rmt2:set_string("remote", meta_rmt1:get_string("local")) + meta_rmt1:set_string("remote", meta_rmt2:get_string("local")) + -- punch remote nodes + minetest.punch_node(pos1) + minetest.punch_node(pos2) + -- set to tube2 + node.name = "hyperloop:tube2" + node.diggable = true + minetest.swap_node(node.pos, node) + -- degrade both nodes + degrade_tupe_node(node1) + degrade_tupe_node(node2) end local function remove_node(pos, node) - ---minetest.remove_node(pos) can't call because "on_destruct" will then be called, too - node.name = "air" - node.diggable = true - minetest.swap_node(pos, node) + ---minetest.remove_node(pos) can't call because "on_destruct" will then be called, too + node.name = "air" + node.diggable = true + minetest.swap_node(pos, node) end -- simple tube without logic or "memory" minetest.register_node("hyperloop:tube2", { - description = "Hyperloop Tube 2", - tiles = { - { - name = "hyperloop_tube_red.png", - animation = { - type = "vertical_frames", - aspect_w = 32, - aspect_h = 32, - length = 2.0, + description = "Hyperloop Tube", + tiles = { + { + name = "hyperloop_tube_locked.png", + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, }, }, - }, - diggable = false, - paramtype2 = "facedir", - groups = {cracky=1, not_in_creative_inventory=1}, - is_ground_content = false, -}) + diggable = false, + paramtype2 = "facedir", + groups = {cracky=1, not_in_creative_inventory=1}, + is_ground_content = false, + }) -- single-node and head-node with meta info about the counter part node for idx = 0,1 do - minetest.register_node("hyperloop:tube"..idx, { - description = "Hyperloop Tube "..idx, - tiles = { - { - name = "hyperloop_tube_green.png", - animation = { - type = "vertical_frames", - aspect_w = 32, - aspect_h = 32, - length = 2.0, - }, - }, - }, + minetest.register_node("hyperloop:tube"..idx, { + description = "Hyperloop Tube", + inventory_image = "hyperloop_tube_inventury.png", + drawtype = "nodebox", + tiles = { + -- up, down, right, left, back, front + 'hyperloop_tube_closed.png', + 'hyperloop_tube_closed.png', + 'hyperloop_tube_closed.png', + 'hyperloop_tube_closed.png', + 'hyperloop_tube_open.png', + 'hyperloop_tube_open.png', + }, - after_place_node = function(pos, placer, itemstack, pointed_thing) - local res, nodes = scan_neighbours(pos) - minetest.chat_send_player(placer:get_player_name(), "res="..res) - local node = minetest.get_node(pos) - node.pos = pos - hyperloop.dbg_nodes(nodes)------------------------------------- - if res == 0 then -- no neighbor available? - starter_node(node) - elseif res == 1 then -- one neighbor available? - head_node(node, nodes[1]) - elseif res == 3 then -- two neighbours available? - minetest.chat_send_player(placer:get_player_name(), "two neighbours") - link_node(node, nodes[1], nodes[2]) - else -- invalid position - minetest.chat_send_player(placer:get_player_name(), "Invalid tube block position") - remove_node(pos, node) - end - end, - - on_destruct = function(pos) - print("on_destruct")------------------------------ - local res, nodes = scan_neighbours(pos) - print("res="..res.." nodes="..dump(nodes))------------------------------ - if res == 4 then - upgrade_node(pos, nodes[1]) - end - end, + after_place_node = function(pos, placer, itemstack, pointed_thing) + local res, nodes = hyperloop.scan_neighbours(pos) + local node = minetest.get_node(pos) + node.pos = pos + if res == 0 then -- no neighbor available? + starter_node(node) + elseif res == 1 then -- one neighbor available? + head_node(node, nodes[1]) + elseif res == 3 then -- two neighbours available? + --minetest.chat_send_player(placer:get_player_name(), "two neighbours") + link_node(node, nodes[1], nodes[2]) + else -- invalid position + minetest.chat_send_player(placer:get_player_name(), "Error: Invalid tube block position!") + remove_node(pos, node) + return itemstack + end + -- for debugging purposes + local meta = minetest.get_meta(pos) + meta:set_string("infotext", meta:get_string("local").." => "..meta:get_string("remote")) + end, - on_punch = function(pos, node, puncher, pointed_thing) - local meta = minetest.get_meta(pos) - loc = meta:get_string("local") - rmt = meta:get_string("remote") - minetest.chat_send_player(puncher:get_player_name(), "local="..loc.." remote="..rmt) - end, - - paramtype2 = "facedir", - groups = {cracky=2, not_in_creative_inventory=idx}, - is_ground_content = false, - drop = "hyperloop:tube0", - }) + on_destruct = function(pos) + local res, nodes = hyperloop.scan_neighbours(pos) + if res == 4 then + upgrade_node(pos, nodes[1]) + end + end, + + -- wake up station node so that it updates its dataset + on_punch = function(pos, node, puncher, pointed_thing) + print("Tube punched") + local res, nodes = hyperloop.scan_for_nodes(pos, "hyperloop:junction") + for _,node in ipairs(nodes) do + print(dump(node.pos)) + minetest.punch_node(node.pos) + -- for debugging purposes + local meta = minetest.get_meta(pos) + meta:set_string("infotext", meta:get_string("local").." => "..meta:get_string("remote")) + end + end, + + paramtype2 = "facedir", + groups = {cracky=2, not_in_creative_inventory=idx}, + is_ground_content = false, + drop = "hyperloop:tube0", + }) end diff --git a/utils.lua b/utils.lua index 8ace4d0..cd40185 100644 --- a/utils.lua +++ b/utils.lua @@ -15,34 +15,44 @@ local PI = 3.1415926 +function table_extend(table1, table2) + for k,v in pairs(table2) do + if (type(table1[k]) == 'table' and type(v) == 'table') then + table_extend(table1[k], v) + else + table1[k] = v + end + end +end + hyperloop.NeighborPos = { - { x=1, y=0, z=0}, - { x=-1, y=0, z=0}, - { x=0, y=1, z=0}, - { x=0, y=-1, z=0}, - { x=0, y=0, z=1}, - { x=0, y=0, z=-1}, + { x=1, y=0, z=0}, + { x=-1, y=0, z=0}, + { x=0, y=1, z=0}, + { x=0, y=-1, z=0}, + { x=0, y=0, z=1}, + { x=0, y=0, z=-1}, } function hyperloop.rad2facedir(yaw) - -- radiant (0..2*PI) to my facedir (0..3) from N, W, S to E - return math.floor((yaw + PI/4) / PI * 2) % 4 + -- radiant (0..2*PI) to my facedir (0..3) from N, W, S to E + return math.floor((yaw + PI/4) / PI * 2) % 4 end function hyperloop.facedir2rad(facedir) - -- my facedir (0..3) from N, W, S to E to radiant (0..2*PI) - return facedir / 2 * PI + -- my facedir (0..3) from N, W, S to E to radiant (0..2*PI) + return facedir / 2 * PI end function hyperloop.facedir2dir(facedir) - -- my facedir (0..3) from N, W, S to E to dir vector - local tbl = { - [0] = { x=0, y=0, z=1}, - [1] = { x=-1, y=0, z=0}, - [2] = { x=0, y=0, z=-1}, - [3] = { x=1, y=0, z=0}, - } - return tbl[facedir % 4] + -- my facedir (0..3) from N, W, S to E to dir vector + local tbl = { + [0] = { x=0, y=0, z=1}, + [1] = { x=-1, y=0, z=0}, + [2] = { x=0, y=0, z=-1}, + [3] = { x=1, y=0, z=0}, + } + return tbl[facedir % 4] end function hyperloop.turnright(dir) @@ -73,35 +83,130 @@ function hyperloop.table2file(filename, table) f:close() end -function hyperloop.store_ring_list() - hyperloop.table2file("hyperloop_ringlist", hyperloop.ringList) +function hyperloop.store_station_list() + hyperloop.table2file("hyperloop_station_list", hyperloop.tAllStations) end - -function hyperloop.dbg_ringlist() - print("RingList:") - print(dump(hyperloop.ringList))------------------------------------------- - for addr,list in ipairs(hyperloop.ringList) do - for idx, pos in ipairs(list) do - print("addr:"..addr.." idx:"..idx.." pos:"..minetest.pos_to_string(pos)) - end - end +-- distance between two points in (tube) blocks +function hyperloop.distance(pos1, pos2) + pos1 = vector.floor(pos1) + pos2 = vector.floor(pos2) + return math.abs(pos1.x - pos2.x) + math.abs(pos1.y - pos2.y) + math.abs(pos1.z - pos2.z) end function hyperloop.dbg_nodes(nodes) - print("Nodes:") - for _,node in ipairs(nodes) do - print("name:"..node.name) - end + print("Nodes:") + for _,node in ipairs(nodes) do + print("name:"..node.name) + end +end + +-- Return true if both blocks given bei string-positions are nearby +function hyperloop.nearby(pos1, pos2) + pos1 = minetest.string_to_pos(pos1) + pos2 = minetest.string_to_pos(pos2) + local pos = vector.subtract(pos1, pos2) + local res = pos.x + pos.y + pos.z + return res == 1 or res == -1 +end + +-- Scan for nodes with the given name in the surrounding +function hyperloop.scan_for_nodes(pos, name) + local nodes = {} + local node, npos + local res = 0 + for _,dir in ipairs(hyperloop.NeighborPos) do + npos = vector.add(pos, dir) + node = minetest.get_node(npos) + if node.name == name then + node.pos = npos + table.insert(nodes, node) + res = res + 1 + end + end + return res, nodes +end + +-- Return station name, which matches the given retoure route +function hyperloop.get_station(tStations, rev_route) + for station,dataSet in pairs(tStations) do + print(station) + for _,route in ipairs(dataSet["routes"]) do + print("get_station "..rev_route[1].." "..route[1].." "..rev_route[2].." "..route[2]) + if rev_route[1] == route[1] and rev_route[2] == route[2] then + return station + end + end + end +end + +-- Return a table with all station names, the given 'sStation' is connected with +-- tRes is used for the resulting table (recursive call) +function hyperloop.get_stations(tStations, sStation, tRes) + if tStations[sStation] == nil then + return nil + end + local dataSet = table.copy(tStations[sStation]) + if dataSet == nil then + return nil + end + tStations[sStation] = nil + for _,route in ipairs(dataSet["routes"]) do + local rev_route = {route[2], route[1]} + local s = hyperloop.get_station(tStations, rev_route) + if s ~= nil then + tRes[#tRes + 1] = s + hyperloop.get_stations(tStations, s, tRes) + end + end + return tRes +end + +-- Return a text block with all station names and their direct connections +function hyperloop.get_stations_as_string(pos) + local sortedList = {} + local distance = 0 + for name, dataSet in pairs(table.copy(hyperloop.tAllStations)) do + distance = hyperloop.distance(pos, minetest.string_to_pos(dataSet["pos"])) + dataSet.name = name + dataSet.distance = distance + sortedList[#sortedList+1] = dataSet + end + table.sort(sortedList, function(x,y) + return x.distance < y.distance + end) + print(dump(sortedList)) + local tRes = {"(player distance: station name (position) => directly connected with)\n\n"} + for _,dataSet in ipairs(sortedList) do + + tRes[#tRes+1] = dataSet.distance + tRes[#tRes+1] = ": " + tRes[#tRes+1] = dataSet.name + tRes[#tRes+1] = " " + tRes[#tRes+1] = dataSet.pos + tRes[#tRes+1] = " => " + for _,route in ipairs(dataSet["routes"]) do + local rev_route = {route[2], route[1]} + local s = hyperloop.get_station(hyperloop.tAllStations, rev_route) + if s ~= nil then + tRes[#tRes + 1] = s + tRes[#tRes + 1] = ", " + end + end + tRes[#tRes] = "\n" + end + return table.concat(tRes) end -- Store and read the RingList to / from a file -- so that upcoming actions are remembered when the game -- is restarted -hyperloop.ringList = hyperloop.file2table("hyperloop_ringlist") +hyperloop.tAllStations = hyperloop.file2table("hyperloop_station_list") -minetest.register_on_shutdown(hyperloop.store_ring_list) +minetest.register_on_shutdown(hyperloop.store_station_list) -- store ring list once a day -minetest.after(60*60*24, hyperloop.store_ring_list) +minetest.after(60*60*24, hyperloop.store_station_list) + +