forked from Reload/techage_modpack
289 lines
6.9 KiB
Lua
289 lines
6.9 KiB
Lua
--[[
|
|
|
|
Hyperloop Mod
|
|
=============
|
|
|
|
Copyright (C) 2017-2019 Joachim Stolberg
|
|
|
|
LGPLv2.1+
|
|
See LICENSE.txt for more information
|
|
|
|
Station and elevator network management
|
|
]]--
|
|
|
|
-- for lazy programmers
|
|
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
|
local P = minetest.string_to_pos
|
|
local M = minetest.get_meta
|
|
|
|
-- Convert to list and add pos based on key string
|
|
local function table_to_list(table)
|
|
local lRes = {}
|
|
for key,item in pairs(table) do
|
|
item.pos = P(key)
|
|
lRes[#lRes+1] = item
|
|
end
|
|
return lRes
|
|
end
|
|
|
|
local function distance(pos1, pos2)
|
|
return math.floor(math.abs(pos1.x - pos2.x) +
|
|
math.abs(pos1.y - pos2.y) + math.abs(pos1.z - pos2.z))
|
|
end
|
|
|
|
-- Add the distance to pos to each list item
|
|
local function add_distance_to_list(lStations, pos)
|
|
for _,item in ipairs(lStations) do
|
|
item.distance = distance(item.pos, pos)
|
|
end
|
|
return lStations
|
|
end
|
|
|
|
-- Add the index to each list item
|
|
local function add_index_to_list(lStations)
|
|
-- walk through the list of floors for the next connection
|
|
local get_next = function(key, idx)
|
|
for _,floor in ipairs(lStations) do
|
|
if floor.conn[6] == key then -- upward match?
|
|
floor.idx = idx
|
|
return S(floor.pos) -- return floor key
|
|
end
|
|
end
|
|
end
|
|
|
|
local key = nil
|
|
for idx = 1,#lStations do
|
|
key = get_next(key, idx)
|
|
end
|
|
return lStations
|
|
end
|
|
|
|
-- Return a table with all stations, the given station (as 'sKey') is connected with
|
|
-- tRes is used for the resulting table (recursive call)
|
|
local function get_stations(tStations, sKey, tRes)
|
|
if not tStations[sKey] or not tStations[sKey].conn then
|
|
return {}
|
|
end
|
|
for dir,dest in pairs(tStations[sKey].conn) do
|
|
-- Not already visited?
|
|
if not tRes[dest] then
|
|
-- Known station?
|
|
if tStations[dest] then
|
|
tStations[dest].name = tStations[dest].name or ""
|
|
tRes[dest] = tStations[dest]
|
|
get_stations(tStations, dest, tRes)
|
|
end
|
|
end
|
|
end
|
|
return tRes
|
|
end
|
|
|
|
-- Return a list with sorted elevators, beginning with the top car
|
|
-- with no shaft upwards
|
|
local function sort_based_on_level(tStations)
|
|
local lStations = table_to_list(table.copy(tStations))
|
|
-- to be able to sort the list, an index has to be added
|
|
lStations = add_index_to_list(lStations)
|
|
table.sort(lStations, function(a,b) return (a.idx or 9999) < (b.idx or 9999) end)
|
|
return lStations
|
|
end
|
|
|
|
-- Return a list with sorted stations
|
|
local function sort_based_on_distance(tStations, pos)
|
|
local lStations = table_to_list(table.copy(tStations))
|
|
-- to be able to sort the list, the distance to pos has to be added
|
|
lStations = add_distance_to_list(lStations, pos)
|
|
table.sort(lStations, function(a,b) return a.distance < b.distance end)
|
|
return lStations
|
|
end
|
|
|
|
-- Return a list with sorted stations
|
|
local function sort_based_on_name(tStations, pos)
|
|
local lStations = table_to_list(table.copy(tStations))
|
|
-- Add distance
|
|
lStations = add_distance_to_list(lStations, pos)
|
|
table.sort(lStations, function(a,b) return a.name < b.name end)
|
|
return lStations
|
|
end
|
|
|
|
|
|
--
|
|
-- Class Network
|
|
--
|
|
|
|
--[[
|
|
tStations["(x,y,z)"] = {
|
|
["conn"] = {
|
|
dir = "(200,0,20)",
|
|
},
|
|
}
|
|
change_counter = n,
|
|
]]--
|
|
|
|
|
|
local Network = {}
|
|
hyperloop.Network = Network
|
|
|
|
function Network:new()
|
|
local o = {
|
|
tStations = {},
|
|
change_counter = 0,
|
|
}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
return o
|
|
end
|
|
|
|
-- Set an elevator or station entry.
|
|
-- tAttr is a table with additional attributes to be stored.
|
|
function Network:set(pos, name, tAttr)
|
|
if pos then
|
|
local sKey = S(pos)
|
|
if not self.tStations[sKey] then
|
|
self.tStations[sKey] = {
|
|
conn = {},
|
|
}
|
|
end
|
|
self.tStations[sKey].name = name or ""
|
|
for k,v in pairs(tAttr) do
|
|
self.tStations[sKey][k] = v
|
|
end
|
|
self.change_counter = self.change_counter + 1
|
|
end
|
|
end
|
|
|
|
-- Update an elevator or station entry.
|
|
-- tAttr is a table with additional attributes to be stored.
|
|
function Network:update(pos, tAttr)
|
|
if pos then
|
|
local sKey = S(pos)
|
|
if self.tStations[sKey] then
|
|
for k,v in pairs(tAttr) do
|
|
if v == "nil" then
|
|
self.tStations[sKey][k] = nil
|
|
else
|
|
self.tStations[sKey][k] = v
|
|
end
|
|
end
|
|
self.change_counter = self.change_counter + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function Network:get(pos)
|
|
return pos and self.tStations[S(pos)]
|
|
end
|
|
|
|
-- Delete an elevator or station entry.
|
|
function Network:delete(pos)
|
|
if pos then
|
|
self.tStations[S(pos)] = nil
|
|
self.change_counter = self.change_counter + 1
|
|
end
|
|
end
|
|
|
|
function Network:changed(counter)
|
|
return self.change_counter > counter, self.change_counter
|
|
end
|
|
|
|
-- Update the connection data base. The output dir information is needed
|
|
-- to be able to delete a connection, if necessary.
|
|
-- Returns true, if data base is changed.
|
|
function Network:update_connections(pos, out_dir, conn_pos)
|
|
local sKey = S(pos)
|
|
local res = false
|
|
if not self.tStations[sKey] then
|
|
self.tStations[sKey] = {}
|
|
res = true
|
|
end
|
|
if not self.tStations[sKey].conn then
|
|
self.tStations[sKey].conn = {}
|
|
res = true
|
|
end
|
|
conn_pos = S(conn_pos)
|
|
if self.tStations[sKey].conn[out_dir] ~= conn_pos then
|
|
self.tStations[sKey].conn[out_dir] = conn_pos
|
|
res = true
|
|
end
|
|
if res then
|
|
self.change_counter = self.change_counter + 1
|
|
end
|
|
return res
|
|
end
|
|
|
|
-- Return the nearest station position
|
|
function Network:get_next_station(pos)
|
|
local min_dist = 999999
|
|
local min_key = nil
|
|
local dist
|
|
for key,item in pairs(self.tStations) do
|
|
if not item.junction then
|
|
dist = distance(pos, P(key))
|
|
if dist < min_dist then
|
|
min_dist = dist
|
|
min_key = key
|
|
end
|
|
end
|
|
end
|
|
return P(min_key)
|
|
end
|
|
|
|
-- Return a sorted list of stations
|
|
-- Param pos: player pos
|
|
-- Param station_pos: next station pos or nil.
|
|
-- Used to generate list with connected stations only
|
|
-- Param sorted: either "dist" or "level"
|
|
function Network:station_list(pos, station_pos, sorted)
|
|
local tStations, lStations
|
|
if station_pos then
|
|
local tRes = {}
|
|
tStations = get_stations(self.tStations, S(station_pos), tRes) -- reduced
|
|
else
|
|
tStations = self.tStations -- all stations
|
|
end
|
|
if sorted == "dist" then
|
|
lStations = sort_based_on_distance(tStations, pos)
|
|
elseif sorted == "level" then
|
|
lStations = sort_based_on_level(tStations)
|
|
else
|
|
-- delete own station from list
|
|
tStations[S(station_pos)] = nil
|
|
lStations = sort_based_on_name(tStations, pos)
|
|
end
|
|
return lStations
|
|
end
|
|
|
|
-- Check the complete table by means of the provided callback bool = func(pos)
|
|
function Network:filter(callback)
|
|
local lKeys = {}
|
|
for key,_ in pairs(self.tStations) do
|
|
lKeys[#lKeys+1] = key
|
|
end
|
|
for _,key in ipairs(lKeys) do
|
|
if not callback(P(key)) then
|
|
self.tStations[key] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
function Network:deserialize(data)
|
|
if data ~= "" then
|
|
data = minetest.deserialize(data)
|
|
self.tStations = data.tStations
|
|
self.change_counter = data.change_counter
|
|
end
|
|
end
|
|
|
|
function Network:serialize()
|
|
return minetest.serialize(self)
|
|
end
|
|
|
|
-- Return a pos/item table with all network nodes, the node at pos is connected with
|
|
function Network:get_node_table(pos)
|
|
local tRes = {}
|
|
local key = S(pos)
|
|
get_stations(self.tStations, key, tRes)
|
|
tRes[key] = nil
|
|
return tRes
|
|
end
|