diff options
Diffstat (limited to 'library/lua/gui/dwarfmode.lua')
| -rw-r--r-- | library/lua/gui/dwarfmode.lua | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua new file mode 100644 index 00000000..c1a8bcb9 --- /dev/null +++ b/library/lua/gui/dwarfmode.lua @@ -0,0 +1,279 @@ +-- Support for messing with the dwarfmode screen + +local _ENV = mkmodule('gui.dwarfmode') + +local gui = require('gui') +local utils = require('utils') + +local dscreen = dfhack.screen +local world_map = df.global.world.map + +AREA_MAP_WIDTH = 23 +MENU_WIDTH = 30 + +function getPanelLayout() + local sw, sh = dscreen.getWindowSize() + local view_height = sh-2 + local view_rb = sw-1 + local area_x2 = sw-AREA_MAP_WIDTH-2 + local menu_x2 = sw-MENU_WIDTH-2 + local menu_x1 = area_x2-MENU_WIDTH-1 + local area_pos = df.global.ui_area_map_width + local menu_pos = df.global.ui_menu_width + local rv = {} + + if area_pos < 3 then + rv.area_map = gui.mkdims_xy(area_x2+1,1,view_rb-1,view_height) + view_rb = area_x2 + end + if menu_pos < area_pos or df.global.ui.main.mode ~= 0 then + if menu_pos >= area_pos then + rv.menu_forced = true + menu_pos = area_pos-1 + end + local menu_x = menu_x2 + if menu_pos < 2 then menu_x = menu_x1 end + rv.menu = gui.mkdims_xy(menu_x+1,1,view_rb-1,view_height) + view_rb = menu_x + end + rv.area_pos = area_pos + rv.menu_pos = menu_pos + rv.map = gui.mkdims_xy(1,1,view_rb-1,view_height) + return rv +end + +function getCursorPos() + if df.global.cursor.x ~= -30000 then + return copyall(df.global.cursor) + end +end + +function setCursorPos(cursor) + df.global.cursor = cursor +end + +function clearCursorPos() + df.global.cursor = xyz2pos(nil) +end + +Viewport = defclass(Viewport) + +function Viewport.make(map,x,y,z) + local self = gui.mkdims_wh(x,y,map.width,map.height) + self.z = z + return mkinstance(Viewport, self) +end + +function Viewport.get(layout) + return Viewport.make( + (layout or getPanelLayout()).map, + df.global.window_x, + df.global.window_y, + df.global.window_z + ) +end + +function Viewport:resize(layout) + return Viewport.make( + (layout or getPanelLayout()).map, + self.x1, self.y1, self.z + ) +end + +function Viewport:set() + local vp = self:clip() + df.global.window_x = vp.x1 + df.global.window_y = vp.y1 + df.global.window_z = vp.z + return vp +end + +function Viewport:clip(x,y,z) + return self:make( + math.max(0, math.min(x or self.x1, world_map.x_count-self.width)), + math.max(0, math.min(y or self.y1, world_map.y_count-self.height)), + math.max(0, math.min(z or self.z, world_map.z_count-1)) + ) +end + +function Viewport:isVisibleXY(target,gap) + gap = gap or 0 + + return math.max(target.x-gap,0) >= self.x1 + and math.min(target.x+gap,world_map.x_count-1) <= self.x2 + and math.max(target.y-gap,0) >= self.y1 + and math.min(target.y+gap,world_map.y_count-1) <= self.y2 +end + +function Viewport:isVisible(target,gap) + gap = gap or 0 + + return self:isVisibleXY(target,gap) and target.z == self.z +end + +function Viewport:centerOn(target) + return self:clip( + target.x - math.floor(self.width/2), + target.y - math.floor(self.height/2), + target.z + ) +end + +function Viewport:scrollTo(target,gap) + gap = math.max(0, gap or 5) + if gap*2 >= math.min(self.width, self.height) then + gap = math.floor(math.min(self.width, self.height)/2) + end + local x = math.min(self.x1, target.x-gap) + x = math.max(x, target.x+gap+1-self.width) + local y = math.min(self.y1, target.y-gap) + y = math.max(y, target.y+gap+1-self.height) + return self:clip(x, y, target.z) +end + +function Viewport:reveal(target,gap,max_scroll,scroll_gap,scroll_z) + gap = math.max(0, gap or 5) + if self:isVisible(target, gap) then + return self + end + + max_scroll = math.max(0, max_scroll or 5) + if self:isVisibleXY(target, -max_scroll) + and (scroll_z or target.z == self.z) then + return self:scrollTo(target, scroll_gap or gap) + else + return self:centerOn(target) + end +end + +MOVEMENT_KEYS = { + CURSOR_UP = { 0, -1, 0 }, CURSOR_DOWN = { 0, 1, 0 }, + CURSOR_LEFT = { -1, 0, 0 }, CURSOR_RIGHT = { 1, 0, 0 }, + CURSOR_UPLEFT = { -1, -1, 0 }, CURSOR_UPRIGHT = { 1, -1, 0 }, + CURSOR_DOWNLEFT = { -1, 1, 0 }, CURSOR_DOWNRIGHT = { 1, 1, 0 }, + CURSOR_UP_FAST = { 0, -1, 0, true }, CURSOR_DOWN_FAST = { 0, 1, 0, true }, + CURSOR_LEFT_FAST = { -1, 0, 0, true }, CURSOR_RIGHT_FAST = { 1, 0, 0, true }, + CURSOR_UPLEFT_FAST = { -1, -1, 0, true }, CURSOR_UPRIGHT_FAST = { 1, -1, 0, true }, + CURSOR_DOWNLEFT_FAST = { -1, 1, 0, true }, CURSOR_DOWNRIGHT_FAST = { 1, 1, 0, true }, + CURSOR_UP_Z = { 0, 0, 1 }, CURSOR_DOWN_Z = { 0, 0, -1 }, + CURSOR_UP_Z_AUX = { 0, 0, 1 }, CURSOR_DOWN_Z_AUX = { 0, 0, -1 }, +} + +function Viewport:scrollByKey(key) + local info = MOVEMENT_KEYS[key] + if info then + local delta = 10 + if info[4] then delta = 20 end + + return self:clip( + self.x1 + delta*info[1], + self.y1 + delta*info[2], + self.z + info[3] + ) + else + return self + end +end + +DwarfOverlay = defclass(DwarfOverlay, gui.Screen) + +function DwarfOverlay:updateLayout() + self.df_layout = getPanelLayout() +end + +function DwarfOverlay:getViewport(old_vp) + if old_vp then + return old_vp:resize(self.df_layout) + else + return Viewport.get(self.df_layout) + end +end + +function DwarfOverlay:moveCursorTo(cursor,viewport) + setCursorPos(cursor) + self:getViewport(viewport):reveal(cursor, 5, 0, 10):set() +end + +function DwarfOverlay:selectBuilding(building,cursor,viewport) + cursor = cursor or utils.getBuildingCenter(building) + + df.global.world.selected_building = building + self:moveCursorTo(cursor, viewport) +end + +function DwarfOverlay:propagateMoveKeys(keys) + for code,_ in pairs(MOVEMENT_KEYS) do + if keys[code] then + self:sendInputToParent(code) + return code + end + end +end + +function DwarfOverlay:simulateViewScroll(keys, anchor, no_clip_cursor) + local layout = self.df_layout + local cursor = getCursorPos() + + anchor = anchor or cursor + + if anchor and keys.A_MOVE_SAME_SQUARE then + self:getViewport():centerOn(anchor):set() + return 'A_MOVE_SAME_SQUARE' + end + + for code,_ in pairs(MOVEMENT_KEYS) do + if keys[code] then + local vp = self:getViewport():scrollByKey(code) + if (cursor and not no_clip_cursor) or no_clip_cursor == false then + vp = vp:reveal(anchor,4,20,4,true) + end + vp:set() + + return code + end + end +end + +function DwarfOverlay:onAboutToShow(below) + local screen = dfhack.gui.getCurViewscreen() + if below then screen = below.parent end + if not df.viewscreen_dwarfmodest:is_instance(screen) then + error("This screen requires the main dwarfmode view") + end +end + +MenuOverlay = defclass(MenuOverlay, DwarfOverlay) + +function MenuOverlay:updateLayout() + DwarfOverlay.updateLayout(self) + self.frame_rect = self.df_layout.menu +end + +MenuOverlay.getWindowSize = gui.FramedScreen.getWindowSize +MenuOverlay.getMousePos = gui.FramedScreen.getMousePos + +function MenuOverlay:onAboutToShow(below) + DwarfOverlay.onAboutToShow(self,below) + + self:updateLayout() + if not self.df_layout.menu then + error("The menu panel of dwarfmode is not visible") + end +end + +function MenuOverlay:onRender() + self:renderParent() + + local menu = self.df_layout.menu + if menu then + -- Paint signature on the frame. + dscreen.paintString( + {fg=COLOR_BLACK,bg=COLOR_DARKGREY}, + menu.x1+1, menu.y2+1, "DFHack" + ) + + self:onRenderBody(gui.Painter.new(menu)) + end +end + +return _ENV |
