I've reached the end of my investigations, and the first post has been updated to reflect the knowledge (and lacks thereof).
I've made a DFHack script to allow for fault finding and comparison with what DF thinks. It's not a "real" script, but an investigation hack, and so is lacking in various forms of quality.
- DF displays the embark entity type as always in reach, while the script uses calculated reach based on settlements.
- The script can't tell "live" necro towers from "dead" ones.
- The summary shows number of entities in each.
- Cycle the various entities with the "p"(revious) and "n"(ext) keys.
local gui = require 'gui'
local dialog = require 'gui.dialogs'
local widgets = require 'gui.widgets'
local guiScript = require 'gui.script'
--================================================================
-- The Grid widget defines an pen supporting X/Y character display grid supporting display of
-- a grid larger than the frame allows through a panning viewport. The init function requires
-- the specification of the width and height attributes that defines the grid dimensions.
-- The grid coordinates are 0 based.
--
Grid = defclass (Grid, widgets.Widget)
Grid.ATTRS =
{width = DEFAULT_NIL,
height = DEFAULT_NIL}
--================================================================
function Grid:init ()
if type (self.width) ~= 'number' or
type (self.height) ~= 'number' or
type (self.frame.w) ~= 'number' or
type (self.frame.h) ~= 'number' or
self.width < 0 or
self.height < 0 then
error ("Grid widgets have to have their widths and heights set permanently on initiation")
return
end
self.grid = dfhack.penarray.new (self.width, self.height)
self.viewport = {x1 = 0,
x2 = self.frame.w - 1,
y1 = 0,
y2 = self.frame.h - 1}
end
--================================================================
function Grid:panTo (x, y)
local x_size = self.viewport.x2 - self.viewport.x1 + 1
local y_size = self.viewport.y2 - self.viewport.y1 + 1
self.viewport.x1 = x
if self.viewport.x1 + x_size > self.width then
self.viewport.x1 = self.width - x_size
end
if self.viewport.x1 < 0 then
self.viewport.x1 = 0
end
self.viewport.x2 = self.viewport.x1 + x_size - 1
self.viewport.y1 = y
if self.viewport.y1 + y_size > self.height then
self.viewport.y1 = self.height - y_size
end
if self.viewport.y1 < 0 then
self.viewport.y1 = 0
end
self.viewport.y2 = self.viewport.y1 + y_size - 1
end
--================================================================
-- Pans the viewport in the X and Y dimensions the number of steps specified by the parameters.
-- It will stop the panning at 0, however, and will not pan outside of the grid (a grid smaller)
-- than the frame will still have non grid parts in the frame, of course).
--
function Grid:pan (x, y)
self:panTo (self.viewport.x1 + x, self.viewport.y1 + y)
end
--================================================================
function Grid:panCenter (x, y)
self:panTo (x - math.floor ((self.viewport.x2 - self.viewport.x1 + 1) / 2),
y - math.floor ((self.viewport.y2 - self.viewport.y1 + 1) / 2))
end
--================================================================
-- Assigns a value to the specified grid (not frame) coordinates. The 'pen'
-- parameter has to be a DFHack 'pen' table or object.
--
function Grid:set (x, y, pen)
if x < 0 or x >= self.width then
error ("Grid:set error: x out of bounds " .. tostring (x) .. " vs 0 - " .. tostring (self.width - 1))
return
elseif y < 0 or y >= self.height then
error ("Grid:set error: y out of bounds " .. tostring (y) .. " vs 0 - " .. tostring (self.height - 1))
return
end
self.grid:set_tile (x, y, pen)
end
--================================================================
-- Returns the data at position x, y in the grid.
--
function Grid:get (x, y)
if x < 0 or x >= self.width then
error ("Grid:set error: x out of bounds " .. tostring (x) .. " vs 0 - " .. tostring (self.width - 1))
return
elseif y < 0 or y >= self.height then
error ("Grid:set error: y out of bounds " .. tostring (y) .. " vs 0 - " .. tostring (self.height - 1))
return
else
return self.grid:get_tile (x, y)
end
end
--================================================================
-- Renders the contents within the viewport into the frame.
--
function Grid:onRenderBody (dc)
local pen
for i = self.frame.l, self.frame.l + self.frame.w - 1 do
for k = self.frame.t, self.frame.t + self.frame.h - 1 do
pen = self.grid:get_tile (self.viewport.x1 + i - self.frame.l,
self.viewport.y1 + k - self.frame.t)
if pen and pen.ch ~= 0 then
dfhack.screen.paintTile (pen, i, k, pen.ch)
end
end
end
end
--================================================================
local civs = {}
local entities = {}
local out_of_range = 30000
function add_neighbor_range (civ_id, x, y, short_range)
local max_range = 30
if short_range then
max_range = 10
end
local site_range = {}
table.insert (site_range, {x, y, 0})
local range_start = 1
local range_end
local local_x
local local_y
if not civs [civ_id] then
-- Prepare range map for the civ, defaulting to "out of reach"
civs [civ_id] = {}
for i = 0, df.global.world.world_data.world_width - 1 do
civs [civ_id] [i] = {}
for k = 0, df.global.world.world_data.world_height - 1 do
civs [civ_id] [i] [k] = out_of_range
end
end
end
civs [civ_id] [x] [y] = 0
for i = 1, max_range do
range_end = #site_range
for k = range_start, range_end do
for x_off = -1, 1 do
local_x = site_range [k] [1] + x_off
for y_off = -1, 1 do
local_y = site_range [k] [2] + y_off
if local_x >= 0 and local_x < df.global.world.world_data.world_width and
local_y >= 0 and local_y < df.global.world.world_data.world_height and
i < civs [civ_id] [local_x] [local_y] then
civs [civ_id] [local_x] [local_y] = i
local region_type = df.global.world.world_data.regions [df.global.world.world_data.region_map [local_x]:_displace (local_y).region_id].type
if region_type == df.world_region_type.Mountains or -- We can reach, but not pass through these, unless
region_type == df.world_region_type.Ocean or -- there's a bridging site on the tile.
region_type == df.world_region_type.Lake then
for k, site in ipairs (df.global.world.world_data.sites) do
if site.pos.x == local_x and
site.pos.y == local_y then
table.insert (site_range, {local_x, local_y, i})
break
end
end
else
table.insert (site_range, {local_x, local_y, i})
end
end
end
end
end
if #site_range == range_end then -- Nothing was added, so we're done
break
end
range_start = range_end + 1
end
end
--------------------------------------
local count_map =
{'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'A',
'B',
'C',
'D',
'E',
'F',
'*'}
function count_character (count)
if count > #count_map then
return count_map [#count_map]
else
return count_map [count]
end
end
--------------------------------------
local count_colors =
{COLOR_WHITE,
COLOR_GREY,
COLOR_BROWN,
COLOR_YELLOW,
COLOR_LIGHTGREEN,
COLOR_GREEN,
COLOR_LIGHTMAGENTA,
COLOR_MAGENTA,
COLOR_LIGHTCYAN,
COLOR_CYAN,
COLOR_LIGHTBLUE,
COLOR_LIGHTRED} -- The rest omitted due to their poor contrast against black.
function count_color (count)
if count <= #count_colors then
return count_colors [count]
else
return count_color [(count % #count_colors) + 1]
end
end
--------------------------------------
function neighbor ()
local civ
local necro_index = #df.global.world.raws.entities
local summary_index = necro_index + 1
local Main_Page = {}
--============================================================
function evaluation ()
for i, site in ipairs (df.global.world.world_data.sites) do
-- ImportantLocation, LairShrine, Camp, Monument assumed not to affect neighbor calculations.
if site.type == df.world_site_type.PlayerFortress or
site.type == df.world_site_type.DarkFortress or
site.type == df.world_site_type.Cave or
site.type == df.world_site_type.MountainHalls or
site.type == df.world_site_type.ForestRetreat or
site.type == df.world_site_type.Town then
local added = false
for k, entity in ipairs (site.entity_links) do
if entity.flags.residence then
if df.global.world.entities.all [entity.entity_id].type == df.historical_entity_type.SiteGovernment then
for l, entity_link in ipairs (df.global.world.entities.all [entity.entity_id].entity_links) do
if entity_link.type == df.entity_entity_link_type.PARENT and
df.global.world.entities.all [entity_link.target].type == df.historical_entity_type.Civilization then
add_neighbor_range (entity_link.target, site.pos.x, site.pos.y, df.global.world.entities.all [entity_link.target].entity_raw.default_site_type == df.world_site_type.Cave) --### Not typed by DFHack
added = true
break
end
end
end
if added then
break
end
end
end
elseif site.type == df.world_site_type.Fortress then -- Assume necro tower
if site.civ_id == site.cur_owner_id and
site.subtype_info.is_tower then -- Don't know how to distinguish dead towers (or other sites) from live ones.
add_neighbor_range (site.civ_id, site.pos.x, site.pos.y, true)
-- dfhack.println (i, site.pos.x, site.pos.y)
end
end
end
for i = 0, summary_index do
for k = 0, df.global.world.world_data.world_width - 1 do
for l = 0, df.global.world.world_data.world_height - 1 do
Main_Page.Region_Grids [i]:set (k, l, {ch = 0, -- Disables display, so color is actually irrelevant
fg = COLOR_BLACK,
bg = COLOR_RED,
bold = false,
tile = nil,
tile_color = false,
tile_fg = nil,
tile_bg = nil})
end
end
end
for i, civ in pairs (civs) do
if df.global.world.entities.all [i].type == df.historical_entity_type.Civilization then
for k = 0, df.global.world.world_data.world_width - 1 do
for l = 0, df.global.world.world_data.world_height - 1 do
if civ [k] [l] ~= out_of_range and
(Main_Page.Region_Grids [df.global.world.entities.all [i].entity_raw.anon_1]:get (k, l).ch == 0 or
Main_Page.Region_Grids [df.global.world.entities.all [i].entity_raw.anon_1]:get (k, l).ch > 48 + civ [k] [l]) then
Main_Page.Region_Grids [df.global.world.entities.all [i].entity_raw.anon_1]:set -- anon_1 is the index into the entity_raw vector.
(k, l, {ch = 48 + civ [k] [l],
fg = COLOR_BLACK,
bg = count_color ((civ [k] [l] % 10) + 1),
bold = false,
tile = nil,
tile_color = false,
tile_fg = nil,
tile_bg = nil})
end
end
end
else -- Should be necro tower
for k = 0, df.global.world.world_data.world_width - 1 do
for l = 0, df.global.world.world_data.world_height - 1 do
if civ [k] [l] ~= out_of_range and
(Main_Page.Region_Grids [necro_index]:get (k, l).ch == 0 or
Main_Page.Region_Grids [necro_index]:get (k, l).ch > 48 + civ [k] [l]) then
Main_Page.Region_Grids [necro_index]:set
(k, l, {ch = 48 + civ [k] [l],
fg = COLOR_BLACK,
bg = count_color ((civ [k] [l] % 10) + 1),
bold = false,
tile = nil,
tile_color = false,
tile_fg = nil,
tile_bg = nil})
end
end
end
end
end
for k = 0, df.global.world.world_data.world_width - 1 do
for l = 0, df.global.world.world_data.world_height - 1 do
local count = 0
for i = 0, necro_index do
if Main_Page.Region_Grids [i].grid:get_tile (k, l).ch ~= 0 then
count = count + 1
end
end
if count > 0 then
Main_Page.Region_Grids [summary_index]:set
(k, l, {ch = count_character (count),
fg = COLOR_BLACK,
bg = count_color (count),
bold = false,
tile = nil,
tile_color = false,
tile_fg = nil,
tile_bg = nil})
end
end
end
--------------------------------------
-- Debugging stuff
if false then
for k = 0, df.global.world.world_data.world_width - 1 do
for l = 0, df.global.world.world_data.world_height - 1 do
local civ = 883 -- The civ we want to examine
if civs [civ] [k] [l] ~= out_of_range then
Main_Page.Region_Grids [necro_index - 1]:set
(k, l, {ch = 48 + (civs [civ] [k] [l] % 10),
fg = COLOR_BLACK,
bg = count_color ((civs [civ] [k] [l] % 10) + 1),
bold = false,
tile = nil,
tile_color = false,
tile_fg = nil,
tile_bg = nil})
end
end
end
end
for i, civ in pairs (civs) do
local count = 0
for x = 0, df.global.world.world_data.world_width - 1 do
for y = 0, df.global.world.world_data.world_height - 1 do
if civ [x] [y] ~= out_of_range then
count = count + 1
end
end
end
dfhack.println (i, df.historical_entity_type [df.global.world.entities.all [i].type], df.global.world.entities.all [i].entity_raw.code, count)
end
end
--============================================================
function center_grids (x, y)
for i = 0, summary_index do
Main_Page.Region_Grids [i]:panCenter (x, y)
end
end
--============================================================
function set_entity_text ()
if Main_Page.Current_Grid == summary_index then
Main_Page.Entity_Text = "Summary"
elseif Main_Page.Current_Grid == necro_index then
Main_Page.Entity_Text = "Necro Towers"
else
Main_Page.Entity_Text = df.global.world.raws.entities [Main_Page.Current_Grid].code
end
Main_Page.Entity:setText (Main_Page.Entity_Text)
end
--============================================================
function Screen_Resized (w, h)
if Main_Page.Region_Grids ~= NIL then
local grid_rect_width
local grid_rect_height
for i = 0, summary_index do
Main_Page.Region_Grids [i].frame.w = math.min (Main_Page.Region_Grids [i].width, math.floor (w / 2) - 24)
Main_Page.Region_Grids [i].frame.h = math.min (Main_Page.Region_Grids [i].height, h - 9)
Main_Page.Region_Grids [i].viewport = {x1 = 0,
x2 = Main_Page.Region_Grids [i].frame.w - 1,
y1 = 0,
y2 = Main_Page.Region_Grids [i].frame.h - 1}
Main_Page.Region_Grids [i]:panCenter (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
end
end
end
--============================================================
Ui = defclass (Ui, gui.FramedScreen)
Ui.ATTRS = {
frame_style = gui.GREY_LINE_FRAME,
frame_title = "Neighbor Indicator",
Resize_Callback = (function (w, h) Screen_Resized (w, h) end),
transparent = true
}
--============================================================
local blink_counter = 0
function Ui:onRenderFrame (dc, rect)
local x1, y1, x2, y2 = rect.x1, rect.y1, rect.x2, rect.y2
if self.transparent then
self:renderParent ()
gui.paint_frame (x1, y1, x2, y2, self.frame_style, self.frame_title)
else
if rect.wgap <= 0 and rect.hgap <= 0 then
dc:clear ()
else
self:renderParent ()
dc:fill (rect, self.frame_background)
end
gui.paint_frame(x1, y1, x2, y2, self.frame_style, self.frame_title)
end
blink_counter = blink_counter + 1
if blink_counter == 10 then
blink_counter = 0
Main_Page.Region_Grids [Main_Page.Current_Grid].visible = not Main_Page.Region_Grids [Main_Page.Current_Grid].visible
end
end
--================================================================
function Ui:onResize (w, h)
self:updateLayout (gui.ViewRect {rect = gui.mkdims_wh (0, 0 , w, h)})
if self.Resize_Callback then
self.Resize_Callback (w, h)
end
end
--================================================================
function Ui:onHelp ()
self.subviews.pages:setSelected (2) --### Don't call this. It doesn't exist...
end
--============================================================
function Ui:init ()
local screen_width, screen_height = dfhack.screen.getWindowSize ()
self.stack = {}
self.item_count = 0
self.keys = {}
Main_Page.Current_Grid = summary_index
Main_Page.Info =
widgets.Label {text = "Cycle with p/n",
frame = {l = 0, t = 18},
text_pen = COLOR_LIGHTBLUE}
Main_Page.Entity_Text = ""
Main_Page.Entity =
widgets.Label {text = " ",
frame = {l = 0, t = 19}}
set_entity_text ()
Main_Page.Region_Grids = {}
for i = 0, summary_index do
Main_Page.Region_Grids [i] = Grid {frame = {l = 18,
t = 2,
w = math.min (df.global.world.worldgen.worldgen_parms.dim_x, math.floor (screen_width / 2) - 24),
h = math.min (df.global.world.worldgen.worldgen_parms.dim_y, screen_height - 9)},
width = df.global.world.worldgen.worldgen_parms.dim_x,
height = df.global.world.worldgen.worldgen_parms.dim_y,
visible = i == Main_Page.Current_Grid}
end
evaluation ()
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
local mainPage = widgets.Panel {
subviews = {}}
for i = 0, summary_index do
table.insert (mainPage.subviews, Main_Page.Region_Grids [i])
table.insert (mainPage.subviews, Main_Page.Info)
table.insert (mainPage.subviews, Main_Page.Entity)
end
----------------------------------------------------------
local pages = widgets.Pages
{subviews = {mainPage},view_id = "pages",
}
pages:setSelected (1)
self:addviews {pages}
end
--============================================================
function Ui:onInput (keys)
if keys.LEAVESCREEN_ALL then
self:dismiss ()
end
if keys.LEAVESCREEN then
self:dismiss ()
end
if keys.CUSTOM_N then
Main_Page.Region_Grids [Main_Page.Current_Grid].visible = false
if Main_Page.Current_Grid == summary_index then
Main_Page.Current_Grid = 0
else
Main_Page.Current_Grid = Main_Page.Current_Grid + 1
end
Main_Page.Region_Grids [Main_Page.Current_Grid].visible = true
set_entity_text ()
elseif keys.CUSTOM_P then
Main_Page.Region_Grids [Main_Page.Current_Grid].visible = false
if Main_Page.Current_Grid == 0 then
Main_Page.Current_Grid = summary_index
else
Main_Page.Current_Grid = Main_Page.Current_Grid - 1
end
Main_Page.Region_Grids [Main_Page.Current_Grid].visible = true
set_entity_text ()
elseif keys.SETUP_LOCAL_X_MUP then
persist_screen:sendInputToParent (df.interface_key.SETUP_LOCAL_X_MUP)
elseif keys.SETUP_LOCAL_X_MDOWN then
persist_screen:sendInputToParent (df.interface_key.SETUP_LOCAL_X_MDOWN)
elseif keys.SETUP_LOCAL_Y_MUP then
persist_screen:sendInputToParent (df.interface_key.SETUP_LOCAL_Y_MUP)
elseif keys.SETUP_LOCAL_Y_MDOWN then
persist_screen:sendInputToParent (df.interface_key.SETUP_LOCAL_Y_MDOWN)
elseif keys.SETUP_LOCAL_X_UP then
persist_screen:sendInputToParent (df.interface_key.SETUP_LOCAL_X_UP)
elseif keys.SETUP_LOCAL_X_DOWN then
persist_screen:sendInputToParent (df.interface_key.SETUP_LOCAL_X_DOWN)
elseif keys.SETUP_LOCAL_Y_UP then
persist_screen:sendInputToParent (df.interface_key.SETUP_LOCAL_Y_UP)
elseif keys.SETUP_LOCAL_Y_DOWN then
persist_screen:sendInputToParent (df.interface_key.SETUP_LOCAL_Y_DOWN)
elseif keys.CURSOR_UPLEFT then
persist_screen:sendInputToParent (df.interface_key.CURSOR_UPLEFT)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_UP then
persist_screen:sendInputToParent (df.interface_key.CURSOR_UP)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_UPRIGHT then
persist_screen:sendInputToParent (df.interface_key.CURSOR_UPRIGHT)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_LEFT then
persist_screen:sendInputToParent (df.interface_key.CURSOR_LEFT)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_RIGHT then
persist_screen:sendInputToParent (df.interface_key.CURSOR_RIGHT)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_DOWNLEFT then
persist_screen:sendInputToParent (df.interface_key.CURSOR_DOWNLEFT)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_DOWN then
persist_screen:sendInputToParent (df.interface_key.CURSOR_DOWN)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_DOWNRIGHT then
persist_screen:sendInputToParent (df.interface_key.CURSOR_DOWNRIGHT)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_UPLEFT_FAST then
persist_screen:sendInputToParent (df.interface_key.CURSOR_UPLEFT_FAST)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_UP_FAST then
persist_screen:sendInputToParent (df.interface_key.CURSOR_UP_FAST)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_UPRIGHT_FAST then
persist_screen:sendInputToParent (df.interface_key.CURSOR_UPRIGHT_FAST)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_LEFT_FAST then
persist_screen:sendInputToParent (df.interface_key.CURSOR_LEFT_FAST)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_RIGHT_FAST then
persist_screen:sendInputToParent (df.interface_key.CURSOR_RIGHT_FAST)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_DOWNLEFT_FAST then
persist_screen:sendInputToParent (df.interface_key.CURSOR_DOWNLEFT_FAST)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_DOWN_FAST then
persist_screen:sendInputToParent (df.interface_key.CURSOR_DOWN_FAST)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.CURSOR_DOWNRIGHT_FAST then
persist_screen:sendInputToParent (df.interface_key.CURSOR_DOWNRIGHT_FAST)
center_grids (df.global.world.world_data.region_details [0].pos.x, df.global.world.world_data.region_details [0].pos.y)
elseif keys.SETUP_BIOME_1 then
persist_screen:sendInputToParent (df.interface_key.SETUP_BIOME_1)
elseif keys.SETUP_BIOME_2 then
persist_screen:sendInputToParent (df.interface_key.SETUP_BIOME_2)
elseif keys.SETUP_BIOME_3 then
persist_screen:sendInputToParent (df.interface_key.SETUP_BIOME_3)
elseif keys.SETUP_BIOME_4 then
persist_screen:sendInputToParent (df.interface_key.SETUP_BIOME_4)
elseif keys.SETUP_BIOME_5 then
persist_screen:sendInputToParent (df.interface_key.SETUP_BIOME_5)
elseif keys.SETUP_BIOME_6 then
persist_screen:sendInputToParent (df.interface_key.SETUP_BIOME_6)
elseif keys.SETUP_BIOME_7 then
persist_screen:sendInputToParent (df.interface_key.SETUP_BIOME_7)
elseif keys.SETUP_BIOME_8 then
persist_screen:sendInputToParent (df.interface_key.SETUP_BIOME_8)
elseif keys.SETUP_BIOME_9 then
persist_screen:sendInputToParent (df.interface_key.SETUP_BIOME_9)
elseif keys.CHANGETAB then
persist_screen:sendInputToParent (df.interface_key.CHANGETAB)
elseif keys.SEC_CHANGETAB then
persist_screen:sendInputToParent (df.interface_key.SEC_CHANGETAB)
end
end
--============================================================
function Show_Viewer ()
local screen = Ui {}
persist_screen = screen
screen:show ()
end
--============================================================
Show_Viewer ()
end
--------------------------------------
neighbor ()