summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-08-20 23:04:01 +0400
committerAlexander Gavrilov2012-08-20 23:04:01 +0400
commit601a3a7927d50500b3dbdd2d20c5d7740a5484a1 (patch)
tree0acd09a61f5b5359d9a4197b8baef8043dda1f38
parent38a07a4ca584e2cccc2c2814c35266ba33d692c8 (diff)
downloaddfhack-601a3a7927d50500b3dbdd2d20c5d7740a5484a1.tar.gz
dfhack-601a3a7927d50500b3dbdd2d20c5d7740a5484a1.tar.bz2
dfhack-601a3a7927d50500b3dbdd2d20c5d7740a5484a1.tar.xz
Add a script that implements a linked mechanism browser.
-rw-r--r--dfhack.init-example3
-rw-r--r--library/LuaApi.cpp23
-rw-r--r--library/lua/gui.lua65
-rw-r--r--library/lua/gui/dwarfmode.lua119
-rw-r--r--scripts/gui/mechanisms.lua143
5 files changed, 350 insertions, 3 deletions
diff --git a/dfhack.init-example b/dfhack.init-example
index 4af4246f..f5f40196 100644
--- a/dfhack.init-example
+++ b/dfhack.init-example
@@ -42,3 +42,6 @@ keybinding add Shift-I "job-material CINNABAR"
keybinding add Shift-B "job-material COBALTITE"
keybinding add Shift-O "job-material OBSIDIAN"
keybinding add Shift-G "job-material GLASS_GREEN"
+
+# browse linked mechanisms
+keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index 112cc4fd..b8c1248b 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -1180,6 +1180,28 @@ static int screen_isDismissed(lua_State *L)
return 1;
}
+static int screen_doSimulateInput(lua_State *L)
+{
+ auto screen = Lua::CheckDFObject<df::viewscreen>(L, 1);
+ luaL_checktype(L, 2, LUA_TTABLE);
+
+ if (!screen)
+ luaL_argerror(L, 1, "NULL screen");
+
+ int sz = lua_rawlen(L, 2);
+ std::set<df::interface_key> keys;
+
+ for (int j = 1; j <= sz; j++)
+ {
+ lua_rawgeti(L, 2, j);
+ keys.insert((df::interface_key)lua_tointeger(L, -1));
+ lua_pop(L, 1);
+ }
+
+ screen->feed(&keys);
+ return 0;
+}
+
}
static const luaL_Reg dfhack_screen_funcs[] = {
@@ -1192,6 +1214,7 @@ static const luaL_Reg dfhack_screen_funcs[] = {
{ "show", &Lua::CallWithCatchWrapper<screen_show> },
{ "dismiss", screen_dismiss },
{ "isDismissed", screen_isDismissed },
+ { "_doSimulateInput", screen_doSimulateInput },
{ NULL, NULL }
};
diff --git a/library/lua/gui.lua b/library/lua/gui.lua
index 0e4e4cc6..5883a2ee 100644
--- a/library/lua/gui.lua
+++ b/library/lua/gui.lua
@@ -4,6 +4,42 @@ local _ENV = mkmodule('gui')
local dscreen = dfhack.screen
+CLEAR_PEN = {ch=32,fg=0,bg=0}
+
+function simulateInput(screen,...)
+ local keys = {}
+ local function push_key(arg)
+ local kv = arg
+ if type(arg) == 'string' then
+ kv = df.interface_key[arg]
+ if kv == nil then
+ error('Invalid keycode: '..arg)
+ end
+ end
+ if type(arg) == 'number' then
+ keys[#keys+1] = kv
+ end
+ end
+ for i = 1,select('#',...) do
+ local arg = select(i,...)
+ if arg ~= nil then
+ local t = type(arg)
+ if type(arg) == 'table' then
+ for k,v in pairs(arg) do
+ if v == true then
+ push_key(k)
+ else
+ push_key(v)
+ end
+ end
+ else
+ push_key(arg)
+ end
+ end
+ end
+ dscreen._doSimulateInput(screen, keys)
+end
+
Screen = defclass(Screen, dfhack.screen)
function Screen.new(attrs)
@@ -26,6 +62,12 @@ function Screen:renderParent()
end
end
+function Screen:inputToParent(...)
+ if self._native and self._native.parent then
+ simulateInput(self._native.parent, ...)
+ end
+end
+
function paint_border(x1,y1,x2,y2,color,title)
local pen = { ch = ' ', fg = COLOR_BLACK, bg = color }
dscreen.fillRect(pen,x1,y1,x2,y1)
@@ -40,20 +82,37 @@ function paint_border(x1,y1,x2,y2,color,title)
end
end
-function Screen:renderFrame(color,title,width,height)
+local function hint_coord(gap,hint)
+ if hint and hint > 0 then
+ return math.min(hint,gap)
+ elseif hint and hint < 0 then
+ return math.max(0,gap-hint)
+ else
+ return math.floor(gap/2)
+ end
+end
+
+function mkdims_xy(x1,y1,x2,y2)
+ return { x1=x1, y1=y1, x2=x2, y2=y2, width=x2-x1+1, height=y2-y1+1 }
+end
+function mkdims_wh(x1,y1,w,h)
+ return { x1=x1, y1=y1, x2=x1+w-1, y2=y1+h-1, width=w, height=h }
+end
+
+function Screen:renderFrame(color,title,width,height,xhint,yhint)
local sw, sh = dscreen.getWindowSize()
local iw, ih = sw-2, sh-2
width = math.min(width or iw, iw)
height = math.min(height or ih, ih)
local gw, gh = iw-width, ih-height
- local x1, y1 = math.floor(gw/2), math.floor(gh/2)
+ local x1, y1 = hint_coord(gw,xhint), hint_coord(gh,yhint)
local x2, y2 = x1+width+1, y1+height+1
if gw == 0 and gh == 0 then
dscreen.clear()
else
self:renderParent()
- dscreen.fillRect({ch=' ',fg=0,bg=0},x1,y1,x2,y2)
+ dscreen.fillRect(CLEAR_PEN,x1,y1,x2,y2)
end
paint_border(x1,y1,x2,y2,color,title)
diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua
new file mode 100644
index 00000000..2c62751e
--- /dev/null
+++ b/library/lua/gui/dwarfmode.lua
@@ -0,0 +1,119 @@
+-- Support for messing with the dwarfmode screen
+
+local _ENV = mkmodule('gui.dwarfmode')
+
+local gui = require('gui')
+local dscreen = dfhack.screen
+
+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 getViewportPos()
+ return {
+ x = df.global.window_x,
+ y = df.global.window_y,
+ z = df.global.window_z
+ }
+end
+
+function getCursorPos()
+ return copyall(df.global.cursor)
+end
+
+function setCursorPos(cursor)
+ df.global.cursor = cursor
+end
+
+function setViewportPos(layout,view)
+ local map = df.global.world.map
+ df.global.window_x = math.max(0, math.min(view.x, map.x_count-layout.map.width))
+ df.global.window_y = math.max(0, math.min(view.y, map.y_count-layout.map.height))
+ df.global.window_z = math.max(0, math.min(view.z, map.z_count-1))
+ return getViewportPos()
+end
+
+function centerViewportOn(layout,target)
+ local mapsz = layout.map
+ local view = xyz2pos(
+ target.x-math.floor(layout.map.width/2),
+ target.y-math.floor(layout.map.height/2),
+ target.z
+ )
+ return setViewportPos(layout, view)
+end
+
+function isInViewport(layout,view,target,gap)
+ gap = gap or 0
+ return target.x-gap >= view.x and target.x+gap < view.x+layout.map.width
+ and target.y-gap >= view.y and target.y+gap < view.y+layout.map.height
+ and target.z == view.z
+end
+
+DwarfOverlay = defclass(DwarfOverlay, gui.Screen)
+
+function DwarfOverlay:updateLayout()
+ self.df_layout = getPanelLayout()
+ self.df_viewport = getViewportPos()
+ self.df_cursor = getCursorPos()
+end
+
+local move_keys = {
+ 'CURSOR_UP', 'CURSOR_DOWN', 'CURSOR_LEFT', 'CURSOR_RIGHT',
+ 'CURSOR_UPLEFT', 'CURSOR_UPRIGHT', 'CURSOR_DOWNLEFT', 'CURSOR_DOWNRIGHT',
+ 'CURSOR_UP_FAST', 'CURSOR_DOWN_FAST', 'CURSOR_LEFT_FAST', 'CURSOR_RIGHT_FAST',
+ 'CURSOR_UPLEFT_FAST', 'CURSOR_UPRIGHT_FAST', 'CURSOR_DOWNLEFT_FAST', 'CURSOR_DOWNRIGHT_FAST',
+ 'CURSOR_UP_Z', 'CURSOR_DOWN_Z', 'CURSOR_UP_Z_AUX', 'CURSOR_DOWN_Z_AUX'
+}
+
+function DwarfOverlay:propagateMoveKeys(keys)
+ for _,v in ipairs(move_keys) do
+ if keys[v] then
+ self:inputToParent(v)
+ return
+ end
+ end
+end
+
+function DwarfOverlay:onIdle()
+ dscreen.invalidate()
+end
+
+function DwarfOverlay:clearMenu()
+ local menu = self.df_layout.menu
+ if not menu then return nil end
+ dscreen.fillRect(gui.CLEAR_PEN,menu.x1,menu.y1,menu.x2,menu.y2)
+ return menu.x1,menu.y1,menu.width,menu.height
+end
+
+return _ENV
diff --git a/scripts/gui/mechanisms.lua b/scripts/gui/mechanisms.lua
new file mode 100644
index 00000000..8c585334
--- /dev/null
+++ b/scripts/gui/mechanisms.lua
@@ -0,0 +1,143 @@
+-- Shows mechanisms linked to the current building.
+
+local gui = require 'gui'
+local guidm = require 'gui.dwarfmode'
+
+function getBuildingName(building)
+ return dfhack.with_temp_object(
+ df.new "string",
+ function(str)
+ building:getName(str)
+ return str.value
+ end
+ )
+end
+
+function listMechanismLinks(building)
+ local lst = {}
+ local function push(item, mode)
+ if item then
+ lst[#lst+1] = { obj = item, name = getBuildingName(item), mode = mode }
+ end
+ end
+
+ push(building, 'self')
+
+ if not df.building_actual:is_instance(building) then
+ return lst
+ end
+
+ local item, tref, tgt
+ for _,v in ipairs(building.contained_items) do
+ item = v.item
+ if df.item_trappartsst:is_instance(item) then
+ tref = dfhack.items.getGeneralRef(item, df.general_ref_type.BUILDING_TRIGGER)
+ if tref then
+ push(tref:getBuilding(), 'trigger')
+ end
+ tref = dfhack.items.getGeneralRef(item, df.general_ref_type.BUILDING_TRIGGERTARGET)
+ if tref then
+ push(tref:getBuilding(), 'target')
+ end
+ end
+ end
+
+ return lst
+end
+
+MechanismList = defclass(MechanismList, guidm.DwarfOverlay)
+
+MechanismList.focus_path = 'mechanisms'
+
+function MechanismList.new(building)
+ local self = {
+ old_cursor = guidm.getCursorPos(),
+ links = listMechanismLinks(building),
+ selected = 1
+ }
+ self.links[1].viewport = guidm.getViewportPos()
+ self.links[1].cursor = guidm.getCursorPos()
+ return mkinstance(MechanismList, self)
+end
+
+local colors = {
+ self = COLOR_CYAN, trigger = COLOR_MAGENTA, target = COLOR_GREEN
+}
+local icons = {
+ self = 128, trigger = 17, target = 16
+}
+
+function MechanismList:onRender()
+ self:renderParent()
+ self:updateLayout()
+ local x,y,w,h = self:clearMenu()
+
+ self.paintString({fg=COLOR_WHITE},x+1,y+1,"Mechanism Links")
+
+ for i,v in ipairs(self.links) do
+ local pen = { fg=colors[v.mode], bold = (i == self.selected) }
+ self.paintTile(pen, x+2, y+2+i, icons[v.mode])
+ self.paintString(pen, x+4, y+2+i, v.name)
+ end
+
+ local nlinks = #self.links
+ local line = y+4+nlinks
+
+ if nlinks <= 1 then
+ self.paintString({fg=COLOR_LIGHTRED},x+1,line,"This building has no links")
+ line = line+2
+ end
+
+ self.paintString({fg=COLOR_LIGHTGREEN},x+1,line,"Esc")
+ self.paintString({fg=COLOR_WHITE},x+4,line,": Back,")
+ self.paintString({fg=COLOR_LIGHTGREEN},x+12,line,"Enter")
+ self.paintString({fg=COLOR_WHITE},x+17,line,": Switch")
+end
+
+function MechanismList:zoomToLink(link)
+ df.global.world.selected_building = link.obj
+
+ local cursor = link.cursor
+ if not cursor then
+ cursor = xyz2pos(link.obj.centerx, link.obj.centery, link.obj.z)
+ end
+ guidm.setCursorPos(cursor)
+
+ if not guidm.isInViewport(self.df_layout, self.df_viewport, cursor, 5) then
+ local vp = link.viewport
+ if vp then
+ guidm.setViewportPos(self.df_layout,vp)
+ else
+ guidm.centerViewportOn(self.df_layout,cursor)
+ end
+ end
+end
+
+function MechanismList:changeSelected(delta)
+ self.selected = 1 + (self.selected + delta - 1) % #self.links
+ self:zoomToLink(self.links[self.selected])
+end
+
+function MechanismList:onInput(keys)
+ if keys.STANDARDSCROLL_UP or keys.SECONDSCROLL_UP then
+ self:changeSelected(-1)
+ elseif keys.STANDARDSCROLL_DOWN or keys.SECONDSCROLL_DOWN then
+ self:changeSelected(1)
+ elseif keys.LEAVESCREEN then
+ if self.selected ~= 1 then
+ self:zoomToLink(self.links[1])
+ end
+ self:dismiss()
+ elseif keys.SELECT then
+ self:dismiss()
+ end
+end
+
+if not df.viewscreen_dwarfmodest:is_instance(dfhack.gui.getCurViewscreen()) then
+ qerror("This script requires the main dwarfmode view")
+end
+if df.global.ui.main.mode ~= df.ui_sidebar_mode.QueryBuilding then
+ qerror("This script requires the 'q' interface mode")
+end
+
+MechanismList.new(df.global.world.selected_building):show()