Skip to content

Commit 8dc50e1

Browse files
committed
Document external TTS usage
1 parent ff1b95a commit 8dc50e1

2 files changed

Lines changed: 203 additions & 0 deletions

File tree

flavor-text.lua

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
-- Writes the currently viewed unit or item flavor text to a file.
2+
--
3+
-- Usage:
4+
-- flavor-text
5+
6+
local folder = 'flavor text'
7+
local filename = 'read flavor.txt'
8+
local filepath = folder .. '/' .. filename
9+
10+
local function clear_output_file()
11+
local ok, err = pcall(dfhack.filesystem.mkdir_recursive, folder)
12+
if not ok then
13+
qerror(('Failed to create folder "%s": %s'):format(folder, err))
14+
end
15+
local file, open_err = io.open(filepath, 'w')
16+
if not file then
17+
qerror(('Failed to open file "%s" for writing: %s'):format(filepath, open_err))
18+
end
19+
file:write('')
20+
file:close()
21+
end
22+
23+
local function reformat(str)
24+
local cleaned = str:gsub('%[B%]', '')
25+
:gsub('%[P%]', '')
26+
:gsub('%[R%]', '')
27+
:gsub('%[C:%d+:%d+:%d+%]', '')
28+
:gsub('%s+', ' ')
29+
:gsub('^%s+', '')
30+
:gsub('%s+$', '')
31+
return cleaned
32+
end
33+
34+
local function collect_lines(entries)
35+
local lines = {}
36+
for _, entry in ipairs(entries) do
37+
if entry.value ~= '' then
38+
local cleaned = reformat(dfhack.df2utf(entry.value))
39+
if cleaned ~= '' then
40+
table.insert(lines, cleaned)
41+
end
42+
end
43+
end
44+
return lines
45+
end
46+
47+
local function get_health_text(view_sheets)
48+
if #view_sheets.unit_health_raw_str == 0 then
49+
return nil
50+
end
51+
local lines = collect_lines(view_sheets.unit_health_raw_str)
52+
if #lines == 0 then
53+
return nil
54+
end
55+
return table.concat(lines, '\n')
56+
end
57+
58+
local function get_personality_text(view_sheets)
59+
if #view_sheets.personality_raw_str == 0 then
60+
return nil
61+
end
62+
local lines = collect_lines(view_sheets.personality_raw_str)
63+
if #lines == 0 then
64+
return nil
65+
end
66+
return table.concat(lines, '\n')
67+
end
68+
69+
local UNIT_SHEET_SUBTAB = {
70+
HEALTH = 2,
71+
PERSONALITY = 10,
72+
}
73+
74+
local HEALTH_ACTIVE_TAB = {
75+
STATUS = 0,
76+
WOUNDS = 1,
77+
TREATMENT = 2,
78+
HISTORY = 3,
79+
DESCRIPTION = 4,
80+
}
81+
82+
local PERSONALITY_ACTIVE_TAB = {
83+
TRAITS = 0,
84+
VALUES = 1,
85+
PREFERENCES = 2,
86+
NEEDS = 3,
87+
}
88+
89+
local function get_unit_flavor_text(view_sheets)
90+
local unit = df.unit.find(view_sheets.active_id)
91+
if not unit then
92+
qerror('Unable to resolve the active unit.')
93+
end
94+
95+
if view_sheets.active_sub_tab == UNIT_SHEET_SUBTAB.HEALTH then
96+
local health_text = get_health_text(view_sheets)
97+
if health_text then
98+
return unit, 'Health', health_text
99+
end
100+
clear_output_file()
101+
qerror('No text found on the Health tab.')
102+
end
103+
104+
if view_sheets.active_sub_tab == UNIT_SHEET_SUBTAB.PERSONALITY
105+
and (view_sheets.personality_active_tab == PERSONALITY_ACTIVE_TAB.TRAITS
106+
or view_sheets.personality_active_tab == PERSONALITY_ACTIVE_TAB.VALUES
107+
or view_sheets.personality_active_tab == PERSONALITY_ACTIVE_TAB.PREFERENCES
108+
or view_sheets.personality_active_tab == PERSONALITY_ACTIVE_TAB.NEEDS)
109+
then
110+
local text = get_personality_text(view_sheets)
111+
if text then
112+
return unit, 'Personality', text
113+
end
114+
clear_output_file()
115+
qerror('No text found on the Personality subtab (Traits/Values/Preferences/Needs).')
116+
end
117+
118+
clear_output_file()
119+
qerror('Open Health, Personality or an item window before running this script.')
120+
end
121+
122+
local function get_item_flavor_text(view_sheets)
123+
local item = dfhack.gui.getSelectedItem(true)
124+
if not item then
125+
qerror('Select an item or open an item view sheet before running this script.')
126+
end
127+
128+
local description = view_sheets.raw_description or ''
129+
if description == '' then
130+
qerror('No item description text found on the item view sheet.')
131+
end
132+
133+
return item, 'Item', reformat(dfhack.df2utf(description))
134+
end
135+
136+
local view_sheets = df.global.game.main_interface.view_sheets
137+
if not view_sheets.open then
138+
clear_output_file()
139+
qerror('Open a unit or item view sheet before running this script.')
140+
end
141+
142+
local screen = dfhack.gui.getDFViewscreen()
143+
local is_unit_sheet = dfhack.gui.matchFocusString('dwarfmode/ViewSheets/UNIT', screen)
144+
local is_item_sheet = dfhack.gui.matchFocusString('dwarfmode/ViewSheets/ITEM', screen)
145+
146+
local subject, flavor_type, text
147+
if is_unit_sheet then
148+
subject, flavor_type, text = get_unit_flavor_text(view_sheets)
149+
elseif is_item_sheet then
150+
subject, flavor_type, text = get_item_flavor_text(view_sheets)
151+
else
152+
clear_output_file()
153+
qerror('Open a unit or item view sheet before running this script.')
154+
end
155+
156+
local ok, err = pcall(dfhack.filesystem.mkdir_recursive, folder)
157+
if not ok then
158+
qerror(('Failed to create folder "%s": %s'):format(folder, err))
159+
end
160+
161+
local file, open_err = io.open(filepath, 'w')
162+
if not file then
163+
qerror(('Failed to open file "%s" for writing: %s'):format(filepath, open_err))
164+
end
165+
166+
file:write(text)
167+
file:close()
168+
169+
local name
170+
if is_unit_sheet then
171+
name = dfhack.df2console(dfhack.units.getReadableName(subject))
172+
else
173+
name = dfhack.df2console(dfhack.items.getDescription(subject, 0, true))
174+
end
175+
176+
print(('Wrote %s flavor text for %s to "%s".'):format(flavor_type, name, filepath))

flavor-text.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
flavor-text
2+
===========
3+
4+
Overview
5+
--------
6+
The ``flavor-text`` script writes the currently viewed unit or item flavor text to
7+
``flavor text/read flavor.txt``.
8+
9+
Usage
10+
-----
11+
Run the script from DFHack:
12+
13+
::
14+
15+
flavor-text
16+
17+
Notes
18+
-----
19+
- The file is overwritten each time the script runs.
20+
- If the wrong window is open, the script clears the output file.
21+
- Supported unit tabs:
22+
- Health (Status/Wounds/Treatment/History/Description)
23+
- Personality (Traits/Values/Preferences/Needs)
24+
- Supported item window: item view sheets.
25+
- You must use your own text-to-speech (TTS) program to read the output.
26+
On Windows 11, Voice Attack works well, and a profile with the launch command
27+
can be included for others to use.

0 commit comments

Comments
 (0)