diff --git a/changelog.txt b/changelog.txt index 3ca33c1fa..d4e7b8557 100644 --- a/changelog.txt +++ b/changelog.txt @@ -36,6 +36,7 @@ Template for new versions: - `gui/spectate`: added "Prefer nicknamed" to the list of options - `gui/mod-manager`: when run in a loaded world, shows a list of active mods -- click to export the list to the clipboard for easy sharing or posting - `gui/blueprint`: now records zone designations +- `gui/journal`: now working on worldmap, before embark -- journal is per world map, stored even if you decided to not embark ## Fixes - `starvingdead`: properly restore to correct enabled state when loading a new game that is different from the first game loaded in this session diff --git a/gui/journal.lua b/gui/journal.lua index 4faacb218..45e78f5e9 100644 --- a/gui/journal.lua +++ b/gui/journal.lua @@ -313,15 +313,17 @@ function JournalScreen:onDismiss() end function main(options) - if not dfhack.isMapLoaded() or (not dfhack.world.isFortressMode() - and not dfhack.world.isAdventureMode()) then - qerror('journal requires a fortress/adventure map to be loaded') + local journal_context_mode = journal_context.detect_journal_context_mode() + + if journal_context_mode == nil then + qerror('journal requires a fortress/adventure/world/legends map to be loaded') end local save_layout = options and options.save_layout local overrided_context_mode = options and options.context_mode + local context_mode = overrided_context_mode == nil and - journal_context.detect_journal_context_mode() or overrided_context_mode + journal_context_mode or overrided_context_mode view = view and view:raise() or JournalScreen{ save_prefix=options and options.save_prefix or '', @@ -330,6 +332,23 @@ function main(options) }:show() end +local last_viewscreen_focus = nil +local SCREEN_SETUP_FORTRESS = 'setupdwarfgame/Default' + +dfhack.onStateChange['gui/journal'] = function (sc) + if view and sc == SC_VIEWSCREEN_CHANGED then + local scr = dfhack.gui.getDFViewscreen(true) + local curr_viewscreen_focus = dfhack.gui.getFocusStrings(scr)[1] + if last_viewscreen_focus == SCREEN_SETUP_FORTRESS and + curr_viewscreen_focus ~= last_viewscreen_focus then + -- hide worldmap journal when embark on fortress is done + view:dismiss() + end + + last_viewscreen_focus = curr_viewscreen_focus + end +end + if not dfhack_flags.module then main() end diff --git a/internal/journal/contexts/worldmap.lua b/internal/journal/contexts/worldmap.lua new file mode 100644 index 000000000..8017f20d2 --- /dev/null +++ b/internal/journal/contexts/worldmap.lua @@ -0,0 +1,74 @@ +--@ module = true + +local json = require 'json' + +local JOURNAL_WELCOME_COPY = [=[ +Welcome to gui/journal, your planning scroll for the world of Dwarf Fortress! + +Here, you can outline your fortress ideas, compare embark sites, or record thoughts before founding your settlement. +The text you write here is saved together with your world - even if you cancel the embark. + +For guidance on navigation and hotkeys, tap the ? button in the upper right corner. +Strike the earth! +]=] + +local TOC_WELCOME_COPY = [=[ +Start a line with # symbols and a space to create a header. For example: + +# My section heading + +or + +## My section subheading + +Those headers will appear here, and you can click on them to jump to them in the text.]=] + +worldmap_config = worldmap_config or json.open('dfhack-config/journal-context.json') + +WorldmapJournalContext = defclass(WorldmapJournalContext) +WorldmapJournalContext.ATTRS{ + save_prefix='', + world_id=DEFAULT_NIL +} + +function get_worldmap_context_key(prefix, world_id) + return prefix .. 'world:' .. world_id +end + +function WorldmapJournalContext:save_content(text, cursor) + if dfhack.isWorldLoaded() then + local key = get_worldmap_context_key(self.save_prefix, self.world_id) + worldmap_config.data[key] = {text={text}, cursor={cursor}} + worldmap_config:write() + end +end + +function WorldmapJournalContext:load_content() + if dfhack.isWorldLoaded() then + local key = get_worldmap_context_key(self.save_prefix, self.world_id) + local worldmap_data = copyall(worldmap_config.data[key] or {}) + + if not worldmap_data.text or #worldmap_data.text[1] == 0 then + worldmap_data.text={''} + worldmap_data.show_tutorial = true + end + worldmap_data.cursor = worldmap_data.cursor or {#worldmap_data.text[1] + 1} + return worldmap_data + end +end + +function WorldmapJournalContext:delete_content() + if dfhack.isWorldLoaded() then + local key = get_worldmap_context_key(self.save_prefix, self.world_id) + table.remove(worldmap_config.data, key) + worldmap_config:write() + end +end + +function WorldmapJournalContext:welcomeCopy() + return JOURNAL_WELCOME_COPY +end + +function WorldmapJournalContext:tocWelcomeCopy() + return TOC_WELCOME_COPY +end diff --git a/internal/journal/journal_context.lua b/internal/journal/journal_context.lua index 989e10f06..d950f4171 100644 --- a/internal/journal/journal_context.lua +++ b/internal/journal/journal_context.lua @@ -1,30 +1,37 @@ --@ module = true local widgets = require 'gui.widgets' -local utils = require('utils') +local utils = require 'utils' local DummyJournalContext = reqscript('internal/journal/contexts/dummy').DummyJournalContext local FortressJournalContext = reqscript('internal/journal/contexts/fortress').FortressJournalContext +local WorldmapJournalContext = reqscript('internal/journal/contexts/worldmap').WorldmapJournalContext local AdventurerJournalContext = reqscript('internal/journal/contexts/adventure').AdventurerJournalContext JOURNAL_CONTEXT_MODE = { FORTRESS='fortress', ADVENTURE='adventure', + WORLDMAP='worldmap', + LEGENDS='legends', DUMMY='dummy' } function detect_journal_context_mode() - if dfhack.world.isFortressMode() then + if not dfhack.isMapLoaded() and dfhack.world.isFortressMode() then + return JOURNAL_CONTEXT_MODE.WORLDMAP + elseif dfhack.isMapLoaded() and dfhack.world.isFortressMode() then return JOURNAL_CONTEXT_MODE.FORTRESS - elseif dfhack.world.isAdventureMode() then + elseif dfhack.isMapLoaded() and dfhack.world.isAdventureMode() then return JOURNAL_CONTEXT_MODE.ADVENTURE + elseif dfhack.world.isLegends() then + return JOURNAL_CONTEXT_MODE.LEGENDS else - qerror('unsupported game mode') + return nil end end function journal_context_factory(journal_context_mode, save_prefix) if journal_context_mode == JOURNAL_CONTEXT_MODE.FORTRESS then - return FortressJournalContext{save_prefix} + return FortressJournalContext{save_prefix=save_prefix} elseif journal_context_mode == JOURNAL_CONTEXT_MODE.ADVENTURE then local interactions = df.global.adventure.interactions if #interactions.party_core_members == 0 or interactions.party_core_members[0] == nil then @@ -34,9 +41,17 @@ function journal_context_factory(journal_context_mode, save_prefix) local adventurer_id = interactions.party_core_members[0] return AdventurerJournalContext{ - save_prefix, + save_prefix=save_prefix, adventurer_id=adventurer_id } + elseif journal_context_mode == JOURNAL_CONTEXT_MODE.WORLDMAP then + local world_id = df.global.world.cur_savegame.world_header.id1 + + return WorldmapJournalContext{save_prefix=save_prefix, world_id=world_id} + elseif journal_context_mode == JOURNAL_CONTEXT_MODE.LEGENDS then + local world_id = df.global.world.cur_savegame.world_header.id1 + + return WorldmapJournalContext{save_prefix=save_prefix, world_id=world_id} elseif journal_context_mode == JOURNAL_CONTEXT_MODE.DUMMY then return DummyJournalContext{} else diff --git a/test/gui/journal.lua b/test/gui/journal.lua index 7d3d101bb..6bcfa7053 100644 --- a/test/gui/journal.lua +++ b/test/gui/journal.lua @@ -237,6 +237,46 @@ function test.restore_text_between_sessions() journal:dismiss() end +function test.restore_text_between_worldmap_sessions() + local journal, text_area = arrange_empty_journal({ + w=80, + context_mode=gui_journal.JOURNAL_CONTEXT_MODE.WORLDMAP + }) + + simulate_input_keys('CUSTOM_CTRL_A') + simulate_input_keys('CUSTOM_DELETE') + + local text = table.concat({ + '60: Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + '112: Sed consectetur, urna sit amet aliquet egestas,', + '60: Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + }, '\n') + + simulate_input_text(text) + simulate_mouse_click(text_area, 10, 1) + + expect.eq(read_rendered_text(text_area), table.concat({ + '60: Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + '112: Sed c_nsectetur, urna sit amet aliquet egestas,', + '60: Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + }, '\n')); + + journal:dismiss() + + journal, text_area = arrange_empty_journal({ + w=80, + context_mode=gui_journal.JOURNAL_CONTEXT_MODE.WORLDMAP + }) + + expect.eq(read_rendered_text(text_area), table.concat({ + '60: Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + '112: Sed c_nsectetur, urna sit amet aliquet egestas,', + '60: Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + }, '\n')); + + journal:dismiss() +end + function test.generate_table_of_contents() local journal, text_area = arrange_empty_journal({w=100, h=10}) @@ -636,3 +676,25 @@ function test.show_fortress_tutorials_on_first_use() expect.str_find('Section 1\n', read_rendered_text(toc_panel)); journal:dismiss() end + +function test.dismiss_on_embark() + -- setupdwarfgame/Default + local journal, text_area, journal_window = arrange_empty_journal({w=125}) + + simulate_input_text(' ') + + expect.eq(read_rendered_text(text_area), ' _'); + + mock.patch(dfhack.gui, 'getFocusStrings', function (pos) + return {'setupdwarfgame/Default'} + end, function () + -- it would be preferable to trigger real view screen somehow, + -- but such simulation is better than nothing + gui_journal.dfhack.onStateChange['gui/journal'](SC_VIEWSCREEN_CHANGED) + end) + + expect.eq(journal:isDismissed(), false) + gui_journal.dfhack.onStateChange['gui/journal'](SC_VIEWSCREEN_CHANGED) + + expect.eq(journal:isDismissed(), true) +end