Skip to content

Commit

Permalink
Add gui/petitions.lua: shows list of fort's petitions (#321)
Browse files Browse the repository at this point in the history
* Add gui/petitions.lua: shows list of fort's petitions

* fix autodocs

* use gui.widgets.Label to simplify text scrolling

Also, minor visual changes

* use `Label.show_scroll_icons`

* remove trailing whitespace

* Update changelog.txt
  • Loading branch information
TymurGubayev authored May 9, 2022
1 parent b808050 commit 64626c8
Show file tree
Hide file tree
Showing 2 changed files with 292 additions and 0 deletions.
2 changes: 2 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ that repo.

## New Scripts

- `gui/petitions`: shows list of fort's petitions

## Fixes

## Misc Improvements
Expand Down
290 changes: 290 additions & 0 deletions gui/petitions.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
-- Show fort's petitions, pending and fulfilled.
--[====[
gui/petitions
=============
Show fort's petitions, pending and fulfilled.
For best experience add following to your ``dfhack*.init``::
keybinding add Alt-P@dwarfmode/Default gui/petitions
]====]

local gui = require 'gui'
local widgets = require 'gui.widgets'
local utils = require 'utils'

-- local args = utils.invert({...})

--[[
[lua]# @ df.agreement_details_type
<type: agreement_details_type>
0 = JoinParty
1 = DemonicBinding
2 = Residency
3 = Citizenship
4 = Parley
5 = PositionCorruption
6 = PlotStealArtifact
7 = PromisePosition
8 = PlotAssassination
9 = PlotAbduct
10 = PlotSabotage
11 = PlotConviction
12 = Location
13 = PlotInfiltrationCoup
14 = PlotFrameTreason
15 = PlotInduceWar
]]

if not dfhack.world.isFortressMode() then return end

-- from gui/unit-info-viewer.lua
do -- for code folding
--------------------------------------------------
---------------------- Time ----------------------
--------------------------------------------------
local TU_PER_DAY = 1200
--[[
if advmode then TU_PER_DAY = 86400 ? or only for cur_year_tick?
advmod_TU / 72 = ticks
--]]
local TU_PER_MONTH = TU_PER_DAY * 28
local TU_PER_YEAR = TU_PER_MONTH * 12

local MONTHS = {
'Granite',
'Slate',
'Felsite',
'Hematite',
'Malachite',
'Galena',
'Limestone',
'Sandstone',
'Timber',
'Moonstone',
'Opal',
'Obsidian',
}
Time = defclass(Time)
function Time:init(args)
self.year = args.year or 0
self.ticks = args.ticks or 0
end
function Time:getDays() -- >>float<< Days as age (including years)
return self.year * 336 + (self.ticks / TU_PER_DAY)
end
function Time:getDayInMonth()
return math.floor ( (self.ticks % TU_PER_MONTH) / TU_PER_DAY ) + 1
end
function Time:getMonths() -- >>int<< Months as age (not including years)
return math.floor (self.ticks / TU_PER_MONTH)
end
function Time:getMonthStr() -- Month as date
return MONTHS[self:getMonths()+1] or 'error'
end
function Time:getDayStr() -- Day as date
local d = math.floor ( (self.ticks % TU_PER_MONTH) / TU_PER_DAY ) + 1
if d == 11 or d == 12 or d == 13 then
d = tostring(d)..'th'
elseif d % 10 == 1 then
d = tostring(d)..'st'
elseif d % 10 == 2 then
d = tostring(d)..'nd'
elseif d % 10 == 3 then
d = tostring(d)..'rd'
else
d = tostring(d)..'th'
end
return d
end
--function Time:__add()
--end
function Time:__sub(other)
if DEBUG then print(self.year,self.ticks) end
if DEBUG then print(other.year,other.ticks) end
if self.ticks < other.ticks then
return Time{ year = (self.year - other.year - 1) , ticks = (TU_PER_YEAR + self.ticks - other.ticks) }
else
return Time{ year = (self.year - other.year) , ticks = (self.ticks - other.ticks) }
end
end
--------------------------------------------------
--------------------------------------------------
end

local we = df.global.ui.group_id

local function getAgreementDetails(a)
local sb = {} -- StringBuilder

sb[#sb+1] = {text = "Agreement #" ..a.id, pen = COLOR_RED}
sb[#sb+1] = NEWLINE

local us = "Us"
local them = "Them"
for i, p in ipairs(a.parties) do
local e_descr = {}
local our = false
for _, e_id in ipairs(p.entity_ids) do
local e = df.global.world.entities.all[e_id]
e_descr[#e_descr+1] = table.concat{"The ", df.historical_entity_type[e.type], " ", dfhack.TranslateName(e.name, true)}
if we == e_id then our = true end
end
if our then
us = table.concat(e_descr, ", ")
else
them = table.concat(e_descr, ", ")
end
end
sb[#sb+1] = them
sb[#sb+1] = NEWLINE
sb[#sb+1] = " petitioned"
sb[#sb+1] = NEWLINE
sb[#sb+1] = us
sb[#sb+1] = NEWLINE
for _, d in ipairs (a.details) do
local petition_date = Time{year = d.year, ticks = d.year_tick}
local petition_date_str = petition_date:getDayStr()..' of '..petition_date:getMonthStr()..' in the year '..tostring(petition_date.year)
local cur_date = Time{year = df.global.cur_year, ticks = df.global.cur_year_tick}
sb[#sb+1] = ("On " .. petition_date_str)
sb[#sb+1] = NEWLINE
local diff = (cur_date - petition_date)
if diff:getDays() < 1.0 then
sb[#sb+1] = ("(this was today)")
elseif diff:getMonths() == 0 then
sb[#sb+1] = ("(this was " .. math.floor( diff:getDays() ) .. " days ago)" )
else
sb[#sb+1] = ("(this was " .. diff:getMonths() .. " months and " .. diff:getDayInMonth() .. " days ago)" )
end
sb[#sb+1] = NEWLINE

sb[#sb+1] = ("Petition type: " .. df.agreement_details_type[d.type])
sb[#sb+1] = NEWLINE
if d.type == df.agreement_details_type.Location then
local details = d.data.Location
sb[#sb+1] = "Provide a "
sb[#sb+1] = {text = df.abstract_building_type[details.type], pen = COLOR_LIGHTGREEN}
sb[#sb+1] = " of tier " .. details.tier
if details.deity_type ~= -1 then
sb[#sb+1] = " of a "
-- None/Deity/Religion
sb[#sb+1] = {text = df.temple_deity_type[details.deity_type], pen = COLOR_LIGHTGREEN}
else
sb[#sb+1] = " for "
sb[#sb+1] = {text = df.profession[details.profession], pen = COLOR_LIGHTGREEN}
end
sb[#sb+1] = NEWLINE
end
end

local petition = {}

if a.flags.petition_not_accepted then
sb[#sb+1] = {text = "This petition wasn't accepted yet!", pen = COLOR_YELLOW}
petition.status = 'PENDING'
elseif a.flags.convicted_accepted then
sb[#sb+1] = {text = "This petition was fulfilled!", pen = COLOR_GREEN}
petition.status = 'FULFILLED'
else
petition.status = 'ACCEPTED'
end

petition.text = sb

return petition
end

local getAgreements = function()
local list = {}

local ags = df.global.world.agreements.all
for i, a in ipairs(ags) do
for _, p in ipairs(a.parties) do
for _, e in ipairs(p.entity_ids) do
if e == we then
list[#list+1] = getAgreementDetails(a)
end
end
end
end

return list
end

local petitions = defclass(petitions, gui.FramedScreen)
petitions.ATTRS = {
frame_style = gui.GREY_LINE_FRAME,
frame_title = 'Petitions',
frame_width = 21, -- is calculated in :refresh
min_frame_width = 21,
frame_height = 16,
frame_inset = 0,
focus_path = 'petitions',
}

function petitions:init(args)
self.list = args.list
-- self.fulfilled = true
self:addviews{
widgets.Label{
view_id = 'text',
frame_inset = 0,
show_scroll_icons = 'right',
},
}

self:refresh()
end

function petitions:refresh()
local lines = {}
-- list of petitions
for _, p in ipairs(self.list) do
if not self.fulfilled and p.status == 'FULFILLED' then goto continue end
-- each petition is a status and a text
for _, tok in ipairs(p.text) do
-- where text is a list of tokens
table.insert(lines, tok)
end
table.insert(lines, NEWLINE)
::continue::
end
table.remove(lines, #lines) -- remove last NEWLINE

local label = self.subviews.text
label:setText(lines)

-- changing text doesn't automatically change scroll position
if label.frame_body then
local last_visible_line = label.start_line_num + label.frame_body.height - 1
if last_visible_line > label:getTextHeight() then
label.start_line_num = math.max(label:getTextHeight() - label.frame_body.height + 1, 1)
end
end

self.frame_width = math.max(label:getTextWidth()+1, self.min_frame_width)
self.frame_width = math.min(df.global.gps.dimx - 2, self.frame_width)
self:onResize(dfhack.screen.getWindowSize()) -- applies new frame_width
end

function petitions:onRenderFrame(painter, frame)
petitions.super.onRenderFrame(self, painter, frame)

painter:seek(frame.x1+2, frame.y1 + frame.height-1):key_string('CUSTOM_F', "toggle fulfilled")
end

function petitions:onInput(keys)
if petitions.super.onInput(self, keys) then return end

if keys.LEAVESCREEN or keys.SELECT then
self:dismiss()
elseif keys.CUSTOM_F then
self.fulfilled = not self.fulfilled
self:refresh()
end
end

df.global.pause_state = true
petitions{list=getAgreements()}:show()

0 comments on commit 64626c8

Please sign in to comment.