local _ENV = mkmodule('plugins.dwarfmonitor') local json = require('json') local guidm = require('gui.dwarfmode') local overlay = require('plugins.overlay') local utils = require('utils') local DWARFMONITOR_CONFIG_FILE = 'dfhack-config/dwarfmonitor.json' -- ------------- -- -- WeatherWidget -- -- ------------- -- WeatherWidget = defclass(WeatherWidget, overlay.OverlayWidget) WeatherWidget.ATTRS{ default_pos={x=15,y=-1}, viewscreens={'dungeonmode', 'dwarfmode'}, } function WeatherWidget:init() self.rain = false self.snow = false end function WeatherWidget:overlay_onupdate() local rain, snow = false, false local cw = df.global.current_weather for i=0,4 do for j=0,4 do weather = cw[i][j] if weather == df.weather_type.Rain then rain = true end if weather == df.weather_type.Snow then snow = true end end end self.frame.w = (rain and 4 or 0) + (snow and 4 or 0) + ((snow and rain) and 1 or 0) self.rain, self.snow = rain, snow end function WeatherWidget:onRenderBody(dc) if self.rain then dc:string('Rain', COLOR_LIGHTBLUE):advance(1) end if self.snow then dc:string('Snow', COLOR_WHITE) end end -- ---------- -- -- DateWidget -- -- ---------- -- local function get_date_format() local ok, config = pcall(json.decode_file, DWARFMONITOR_CONFIG_FILE) if not ok or not config.date_format then return 'Y-M-D' end return config.date_format end DateWidget = defclass(DateWidget, overlay.OverlayWidget) DateWidget.ATTRS{ default_pos={x=-16,y=1}, viewscreens={'dungeonmode', 'dwarfmode'}, } function DateWidget:init() self.datestr = '' self.fmt = get_date_format() end function DateWidget:overlay_onupdate() local year = dfhack.world.ReadCurrentYear() local month = dfhack.world.ReadCurrentMonth() + 1 local day = dfhack.world.ReadCurrentDay() local fmt = self.fmt local datestr = 'Date:' for i=1,#fmt do local c = fmt:sub(i, i) if c == 'y' or c == 'Y' then datestr = datestr .. year elseif c == 'm' or c == 'M' then if c == 'M' and month < 10 then datestr = datestr .. '0' end datestr = datestr .. month elseif c == 'd' or c == 'D' then if c == 'D' and day < 10 then datestr = datestr .. '0' end datestr = datestr .. day else datestr = datestr .. c end end self.frame.w = #datestr self.datestr = datestr end function DateWidget:onRenderBody(dc) dc:string(self.datestr, COLOR_GREY) end -- ------------ -- -- MiseryWidget -- -- ------------ -- MiseryWidget = defclass(MiseryWidget, overlay.OverlayWidget) MiseryWidget.ATTRS{ default_pos={x=-2,y=-1}, viewscreens={'dwarfmode'}, } function MiseryWidget:init() self.colors = getStressCategoryColors() self.stress_category_counts = {} self.frame.w = 2*#self.colors + 1 end function MiseryWidget:overlay_onupdate() local counts, num_colors = {}, #self.colors for _,unit in ipairs(df.global.world.units.active) do if not dfhack.units.isCitizen(unit, true) then goto continue end local stress_category = math.min(num_colors, dfhack.units.getStressCategory(unit)+1) counts[stress_category] = (counts[stress_category] or 0) + 1 ::continue:: end local width = 2 + num_colors - 1 -- 'H:' plus the slashes for i=1,num_colors do width = width + #tostring(counts[i] or 0) end self.stress_category_counts = counts self.frame.w = width end function MiseryWidget:onRenderBody(dc) dc:string('H:', COLOR_WHITE) local counts = self.stress_category_counts for i,color in ipairs(self.colors) do dc:string(tostring(counts[i] or 0), color) if i < #self.colors then dc:string('/', COLOR_WHITE) end end end -- ------------ -- -- CursorWidget -- -- ------------ -- CursorWidget = defclass(CursorWidget, overlay.OverlayWidget) CursorWidget.ATTRS{ default_pos={x=2,y=-4}, viewscreens={'dungeonmode', 'dwarfmode'}, type='all', short=false, } CursorWidget.COORD_TYPES = utils.OrderedTable() CursorWidget.COORD_TYPES.mouse_ui = { description = 'mouse UI grid', get_coords = function() return xyz2pos(dfhack.screen.getMousePos()) end, } CursorWidget.COORD_TYPES.mouse_map = { description = 'mouse map coord', get_coords = dfhack.gui.getMousePos, } CursorWidget.COORD_TYPES.keyboard_map = { description = 'kbd cursor coord', get_coords = guidm.getCursorPos, } function CursorWidget:init() local ok, config = pcall(json.decode_file, DWARFMONITOR_CONFIG_FILE) if ok then self.type = tostring(config.coords_type or self.type) self.short = config.coords_short else dfhack.printerr(('Failed to load %s: %s'):format(DWARFMONITOR_CONFIG_FILE, config)) end end function CursorWidget:onRenderBody(dc) local text = {} for k, v in pairs(CursorWidget.COORD_TYPES) do -- fall back to 'all' behavior if type isn't recognized if k == self.type or not CursorWidget.COORD_TYPES[self.type] then local coords = v.get_coords() if coords then local prefix = self.short and '' or (v.description .. ' ') local coord_str = table.concat({coords.x, coords.y, coords.z}, ',') table.insert(text, ('%s(%s)'):format(prefix, coord_str)) end end end local width = 0 for i,line in ipairs(text) do dc:seek(0, i-1):string(line) width = math.max(width, #line) end self.frame.w = width self.frame.h = #text end -- register our widgets with the overlay OVERLAY_WIDGETS = { cursor=CursorWidget, date=DateWidget, misery=MiseryWidget, weather=WeatherWidget, } return _ENV