diff --git a/docs/markdown.rst b/docs/markdown.rst index bffc5cfba6..94a764b284 100644 --- a/docs/markdown.rst +++ b/docs/markdown.rst @@ -2,56 +2,106 @@ markdown ======== .. dfhack-tool:: - :summary: Exports the text you see on the screen for posting online. - :tags: unavailable - -This tool saves a copy of a text screen, formatted in markdown, for posting to -Reddit (among other places). See `forum-dwarves` if you want to export BBCode -for posting to the Bay 12 forums. - -This script will attempt to read the current screen, and if it is a text -viewscreen (such as the dwarf 'thoughts' screen or an item 'description') then -append a marked-down version of this text to the output file. Previous entries -in the file are not overwritten, so you may use the ``markdown`` command -multiple times to create a single document containing the text from multiple -screens, like thoughts from several dwarves or descriptions from multiple -artifacts. - -The screens which have been tested and known to function properly with this -script are: - -#. dwarf/unit 'thoughts' screen -#. item/art 'description' screen -#. individual 'historical item/figure' screens -#. manual pages -#. announcements screen -#. combat reports screen -#. latest news (when meeting with liaison) - -There may be other screens to which the script applies. It should be safe to -attempt running the script with any screen active. An error message will inform -you when the selected screen is not appropriate for this script. + :summary: Save descriptions of selected units/items in markdown format (e.g., for Reddit). + :tags: fort | inspection | units + +Saves descriptions of selected units or items to a markdown file. + + +For units, the script retrieves: +#. Name, race, age, profession +#. Description from the Unit/Health/Description screen +#. Traits from the Unit/Personality/Traits screen +The script works for all units with text in the Description &/or Personality/Traits tabs, +such as dwarves, dogs, elves, goblins, and beasts. + +For items, it retrieves: +#. Decorated name (e.g., "☼«☼Chalk Statue of Dakas☼»☼") +#. Full description from the item's view sheet +The script works for most items with in-game descriptions and names, including those in storage, +on the ground, installed as a building, or worn/carried by units. + +The script appends a markdown-formatted version of the text to a target file +for easy sharing, e.g., on Reddit. + +By default, entries are appended, not overwritten, allowing the ``markdown`` command +to compile descriptions of multiple items & units in a single document. + +By default, data is stored in markdown_{YourWorldName}_export.md. + +See `forum-dwarves` for BBCode export (e.g., for the Bay12 Forums). + Usage ----- :: - markdown [-n] [] + markdown [-o] [] + +Appends the description of the selected unit/item +to ``markdown_{world_name}_export.md`` file by default. +Specifying a filename will append to ``markdown_{filename}.md`` instead, +which is useful for organizing data by category or topic. +The [-o] argument tells the script to overwrite the output file. + +Options +------- -The output is appended to the ``md_export.md`` file by default. If an alternate -name is specified, then a file named like ``md_{name}.md`` is used instead. +``-o`` + Overwrite the output file, deleting previous entries. +``help`` + Show help. Examples -------- -``markdown`` - Appends the contents of the current screen to the ``md_export.md`` file. -``markdown artifacts`` - Appends the contents of the current screen to the ``md_artifacts.md`` file. +:: -Options -------- + markdown + +Example output for a selected chalk statue in the world "Orid Tamun", appended to the default ``markdown_Orid_Tamun_export.md`` file: + + [...previous entries...] + + ### ☼Chalk Statue of Bìlalo Bandbeach☼ + + #### Description: + This is a well-crafted chalk statue of Bìlalo Bandbeach. The item is an image of + Bìlalo Bandbeach the elf and Lani Lyricmonks the Learned the ettin in chalk by + Domas Uthmiklikot. Lani Lyricmonks the Learned is striking down Bìlalo Bandbeach. + The artwork relates to the killing of the elf Bìlalo Bandbeach by the + ettin Lani Lyricmonks the Learned with Hailbite in The Forest of Indignation in 147. + + --- +:: + + ``markdown -o descriptions`` + +Example output for a selected unit Lokum Alnisendok, written to the newly overwritten ``markdown_descriptions.md`` file: +:: + + ### Lokum Alnisendok, dwarf, 27 years old Presser. + + #### Description: + A short, sturdy creature fond of drink and industry. + + He is very quick to tire. + + His very long beard is neatly combed. His very long sideburns are braided. + His very long moustache is neatly combed. His hair is clean-shaven. He is average in size. + His nose is sharply hooked. His nose bridge is convex. His gold eyes are slightly wide-set. + His somewhat tall ears are somewhat narrow. His hair is copper. His skin is copper. + + #### Personality: + He has an amazing memory, but he has a questionable spatial sense and poor focus. + + He doesn't generally think before acting. He feels a strong need to reciprocate any favor done for him. + He enjoys the company of others. He does not easily hate or develop negative feelings. He generally + finds himself quite hopeful about the future. He tends to be swayed by the emotions of others. + He finds obligations confining, though he is conflicted by this for more than one reason. He doesn't + tend to hold on to grievances. He has an active imagination. + + He needs alcohol to get through the working day. -``-n`` - Overwrite the contents of output file instead of appending. + --- \ No newline at end of file diff --git a/markdown.lua b/markdown.lua index 5ca9f03749..f191c5cdca 100644 --- a/markdown.lua +++ b/markdown.lua @@ -1,227 +1,138 @@ --- Save a text screen in markdown (eg for reddit) +-- Save selected unit/item' description in markdown (e.g., for reddit) +-- This script extracts descriptions of selected units or items and saves them in markdown format. -- This is a derivatiwe work based upon scripts/forum-dwarves.lua by Caldfir and expwnent -- Adapted for markdown by Mchl https://github.com/Mchl -local helpstr = [====[ - -markdown -======== -Save a copy of a text screen in markdown (useful for Reddit, among other sites). -See `forum-dwarves` for BBCode export (for e.g. the Bay12 Forums). - -This script will attempt to read the current df-screen, and if it is a -text-viewscreen (such as the dwarf 'thoughts' screen or an item / creature -'description') or an announcement list screen (such as announcements and -combat reports) then append a marked-down version of this text to the -target file (for easy pasting on reddit for example). -Previous entries in the file are not overwritten, so you -may use the``markdown`` command multiple times to create a single -document containing the text from multiple screens (eg: text screens -from several dwarves, or text screens from multiple artifacts/items, -or some combination). - -Usage:: - - markdown [-n] [filename] - -:-n: overwrites contents of output file -:filename: - if provided, save to :file:`md_{filename}.md` instead - of the default :file:`md_export.md` - -The screens which have been tested and known to function properly with -this script are: - -#. dwarf/unit 'thoughts' screen -#. item/art 'description' screen -#. individual 'historical item/figure' screens -#. manual -#. announements screen -#. combat reports screen -#. latest news (when meeting with liaison) - -There may be other screens to which the script applies. It should be -safe to attempt running the script with any screen active, with an -error message to inform you when the selected screen is not appropriate -for this script. - -]====] +-- Updated to work with Steam version by Glotov4 https://github.com/glotov4 -local args = {...} - -if args[1] == 'help' then - print(helpstr) - return -end - -local writemode = 'a' - --- check if we want to append to an existing file (default) or overwrite previous contents -if args[1] == '-n' or args[1] == '/n' then - writemode = 'w' - table.remove(args, 1) -end +local utils = require('utils') +local gui = require('gui') +local worldName = dfhack.df2utf(dfhack.TranslateName(df.global.world.world_data.name)):gsub(" ", "_") +-- Argument processing +local args = {...} +local writemode = 'a' -- default to append mode +local overwriteFlag = '-o' local filename +local userProvidedNameParts = {} + +-- Check for the overwrite flag and remove it from the arguments if found +for i = #args, 1, -1 do + if args[i] == overwriteFlag then + writemode = 'w' -- set to overwrite mode + table.remove(args, i) -- remove the overwrite flag from the arguments + break -- remove only the first occurrence of the overwrite flag + end +end -if args[1] ~= nil then - filename = 'md_' .. table.remove(args, 1) .. '.md' +-- Join remaining arguments with underscores to create the user-provided filename +if #args > 0 then + local userProvidedName = table.concat(args, "_") + filename = 'markdown_' .. userProvidedName .. '.md' else - filename = 'md_export.md' + filename = 'markdown_' .. worldName .. '_export.md' end -local utils = require 'utils' -local gui = require 'gui' -local dialog = require 'gui.dialogs' - -local scrn = dfhack.gui.getCurViewscreen() -local flerb = dfhack.gui.getFocusString(scrn) - -local months = { - [1] = 'Granite', - [2] = 'Slate', - [3] = 'Felsite', - [4] = 'Hematite', - [5] = 'Malachite', - [6] = 'Galena', - [7] = 'Limestone', - [8] = 'Sandstone', - [9] = 'Timber', - [10] = 'Moonstone', - [11] = 'Opal', - [12] = 'Obsidian', -} - +-- Utility functions local function getFileHandle() - return io.open(filename, writemode) + return assert(io.open(filename, writemode), "Error opening file: " .. filename) end local function closeFileHandle(handle) - handle:write('\n***\n\n') + handle:write('\n---\n\n') handle:close() - print ('Data exported to "' .. filename .. '"') + local function closeFileHandle(handle) + handle:write('\n---\n\n') + handle:close() + if writemode == 'a' then + print('\nData appended to "' .. filename .. '"') + elseif writemode == 'w' then + print('\nData overwritten in "' .. filename .. '"') + end + end end -local function reformat(strin) - local strout = strin - - -- [P] tags seem to indicate a new paragraph - local newline_idx = string.find(strout, '[P]', 1, true) - while newline_idx ~= nil do - strout = string.sub(strout, 1, newline_idx - 1) .. '\n***\n\n' .. string.sub(strout, newline_idx + 3) - newline_idx = string.find(strout, '[P]', 1, true) - end +local function reformat(str) + -- [B] tags seem to indicate a new paragraph + -- [R] tags seem to indicate a sub-blocks of text.Treat them as paragraphs. + -- [P] tags seem to be redundant + -- [C] tags indicate color. Remove all color information + return str:gsub('%[B%]', '\n\n') + :gsub('%[R%]', '\n\n') + :gsub('%[P%]', '') + :gsub('%[C:%d+:%d+:%d+%]', '') + :gsub('\n\n+', '\n\n') +end - -- [R] tags seem to indicate a new 'section'. Let's mark it with a horizontal line. - newline_idx = string.find(strout, '[R]', 1, true) - while newline_idx ~= nil do - strout = string.sub(strout, 1, newline_idx - 1) .. '\n***\n\n' .. string.sub(strout,newline_idx + 3) - newline_idx = string.find(strout, '[R]', 1, true) - end +local function getNameRaceAgeProf(unit) + --%s is a placeholder for a string, and %d is a placeholder for a number. + return string.format("%s, %d years old %s.", dfhack.units.getReadableName(unit), df.global.cur_year - unit.birth_year, dfhack.units.getProfessionName(unit)) +end - -- No idea what [B] tags might indicate. Just removing them seems to work fine - newline_idx = string.find(strout, '[B]', 1, true) - while newline_idx ~= nil do - strout = string.sub(strout, 1, newline_idx - 1) .. string.sub(strout,newline_idx + 3) - newline_idx = string.find(strout, '[B]', 1, true) - end +-- Main logic for item and unit processing +local item = dfhack.gui.getSelectedItem(true) +local unit = dfhack.gui.getSelectedUnit(true) - -- Reddit doesn't support custom colors in markdown. We need to remove all color information :( - local color_idx = string.find(strout, '[C:', 1, true) - while color_idx ~= nil do - strout = string.sub(strout, 1, color_idx - 1) .. string.sub(strout, color_idx + 9) - color_idx = string.find(strout, '[C:', 1, true) - end +if not item and not unit then + print([[ +Error: No unit or item is currently selected. +- To select a unit, click on it. +- For items that are installed as buildings (like statues or beds), +open the building's interface and click the magnifying glass icon. - return strout +Please select a valid target and try running the script again.]]) + -- Early return to avoid proceeding further if no unit or item is selected + return end -local function formattime(year, ticks) - -- Dwarf Mode month is 33600 ticks long - local month = math.floor(ticks / 33600) - local dayRemainder = ticks - month * 33600 - - -- Dwarf Mode day is 1200 ticks long - local day = math.floor(dayRemainder / 1200) - local timeRemainder = dayRemainder - day * 1200 - - -- Assuming a 24h day each Dwarf Mode tick corresponds to 72 seconds - local seconds = timeRemainder * 72 - - local H = string.format("%02.f", math.floor(seconds / 3600)); - local m = string.format("%02.f", math.floor(seconds / 60 - (H * 60))); - local i = string.format("%02.f", math.floor(seconds - H * 3600 - m * 60)); - - day = day + 1 - if (day == 1 or day == 21) then - day = day .. 'st' --luacheck: retype - elseif (day == 2 or day == 22) then - day = day .. 'nd' --luacheck: retype - elseif (day == 3 or day == 23) then - day = day .. 'rd' --luacheck: retype - else - day = day .. 'th' --luacheck: retype - end +local log = getFileHandle() - return (day .. " " .. months[month + 1] .. " " .. year .. " " .. H .. ":" .. m..":" .. i) -end +if item then + -- Item processing + local itemRawName = dfhack.items.getDescription(item, 0, true) + local itemRawDescription = df.global.game.main_interface.view_sheets.raw_description + log:write('### ' .. dfhack.df2utf(itemRawName) .. '\n\n#### Description: \n' .. reformat(dfhack.df2utf(itemRawDescription)) .. '\n') + print('Exporting description of the ' .. itemRawName) -if flerb == 'textviewer' then - local scrn = scrn --as:df.viewscreen_textviewerst +elseif unit then + -- Unit processing + -- Simulate UI interactions to load data into memory (click through tabs). Note: Constant might change with DF updates/patches + local screen = dfhack.gui.getDFViewscreen() + local windowSize = dfhack.screen.getWindowSize() - local lines = scrn.src_text + -- Click "Personality" + local personalityWidthConstant = 48 + local personalityHeightConstant = 11 - if lines ~= nil then + df.global.gps.mouse_x = windowSize - personalityWidthConstant + df.global.gps.mouse_y = personalityHeightConstant - local log = getFileHandle() - log:write('### ' .. dfhack.df2utf(scrn.title) .. '\n') + gui.simulateInput(screen, '_MOUSE_L') - print('Exporting ' .. dfhack.df2console(scrn.title) .. '\n') + -- Click "Health" + local healthWidthConstant = 74 + local healthHeightConstant = 13 - for n,x in ipairs(lines) do - log:write(reformat(dfhack.df2utf(x.value)).." ") --- debug output --- print(x.value) - end - closeFileHandle(log) - end + df.global.gps.mouse_x = windowSize - healthWidthConstant + df.global.gps.mouse_y = healthHeightConstant -elseif flerb == 'announcelist' then - local scrn = scrn --as:df.viewscreen_announcelistst - - local lines = scrn.reports - - if lines ~= nil then - local log = getFileHandle() - local lastTime = "" - - for n,x in ipairs(lines) do - local currentTime = formattime(x.year, x.time) - if (currentTime ~= lastTime) then - lastTime = currentTime - log:write('\n***\n\n') - log:write('## ' .. currentTime .. '\n') - end --- debug output --- print(x.text) - log:write(x.text .. '\n') - end - closeFileHandle(log) - end + gui.simulateInput(screen, '_MOUSE_L') + -- Click "Health/Description" + local healthDescriptionWidthConstant = 51 + local healthDescriptionHeightConstant = 15 -elseif flerb == 'topicmeeting' then - local lines = scrn.text --hint:df.viewscreen_topicmeetingst + df.global.gps.mouse_x = windowSize - healthDescriptionWidthConstant + df.global.gps.mouse_y = healthDescriptionHeightConstant - if lines ~= nil then - local log = getFileHandle() + gui.simulateInput(screen, '_MOUSE_L') - for n,x in ipairs(lines) do --- debug output --- print(x.value) - log:write(x.value .. '\n') - end - closeFileHandle(log) + local unit_description_raw = df.global.game.main_interface.view_sheets.unit_health_raw_str[0].value + local unit_personality_raw = df.global.game.main_interface.view_sheets.personality_raw_str + + log:write('### ' .. dfhack.df2utf(getNameRaceAgeProf(unit)) .. '\n\n#### Description: \n' .. reformat(dfhack.df2utf(unit_description_raw)) .. '\n\n#### Personality: \n') + for _, unit_personality in ipairs(unit_personality_raw) do + log:write(reformat(dfhack.df2utf(unit_personality.value)) .. '\n') end -else - print 'This is not a textview, announcelist or topicmeeting screen. Can\'t export data, sorry.' -end + print('Exporting Health/Description & Personality/Traits data for: \n' .. dfhack.df2console(getNameRaceAgeProf(unit))) +else end +closeFileHandle(log)