123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- --[[
- 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 = {}
- vents.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
|