diff --git a/changelog.txt b/changelog.txt index 38f2477022..a760edab2c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -44,6 +44,7 @@ Template for new versions: - `gui/notify`: new notification type: injured citizens; click to zoom to injured units; also displays a warning if your hospital is not functional (or if you have no hospital) - `prioritize`: new info panel on under-construction buildings showing if the construction job has been taken and by whom. click to zoom to builder; toggle high priority status for job if it's not yet taken and you need it to be built ASAP - `gui/pathable`: new "Depot" mode that shows whether wagons can path to your trade depot +- `advtools`: automatically add a conversation option to "ask whereabouts of" for all your relationships (before, you could only ask whereabouts of people involved in rumors) - `gui/design`: all-new visually-driven UI for much improved usability ## Fixes diff --git a/docs/advtools.rst b/docs/advtools.rst index 6c904f36c4..3be128d4de 100644 --- a/docs/advtools.rst +++ b/docs/advtools.rst @@ -35,4 +35,7 @@ framework. They can be repositioned via `gui/overlay` or toggled via When enabled, this overlay will automatically add additional searchable keywords to conversation topics. In particular, topics that relate to slain -enemies will gain the ``slay`` and ``kill`` keywords. +enemies will gain the ``slay`` and ``kill`` keywords. It will also add additional +conversation options for asking whereabouts of your relationships - in vanilla, +you can only ask whereabouts of historical figures involved in rumors you personally +witnessed or heard about. diff --git a/internal/advtools/convo.lua b/internal/advtools/convo.lua index cf2fc1a9bc..210d94854b 100644 --- a/internal/advtools/convo.lua +++ b/internal/advtools/convo.lua @@ -13,7 +13,7 @@ local adventure = df.global.game.main_interface.adventure -- Gets the keywords already present on the dialog choice local function getKeywords(choice) local keywords = {} - for _, keyword in ipairs(choice.key_word) do + for _, keyword in ipairs(choice.keywords) do table.insert(keywords, keyword.value:lower()) end return keywords @@ -22,8 +22,8 @@ end -- Adds a keyword to the dialog choice local function addKeyword(choice, keyword) local keyword_ptr = df.new('string') - keyword_ptr.value = keyword - choice.key_word:insert('#', keyword_ptr) + keyword_ptr.value = dfhack.toSearchNormalized(keyword) + choice.keywords:insert('#', keyword_ptr) end -- Adds multiple keywords to the dialog choice @@ -45,7 +45,7 @@ local function generateKeywordsForChoice(choice) end -- generate keywords from useful words in the text - for _, data in ipairs(choice.print_string.text) do + for _, data in ipairs(choice.title.text) do for word in dfhack.toSearchNormalized(data.value):gmatch('%w+') do -- collect additional keywords based on the special words if word == 'slew' or word == 'slain' then @@ -61,8 +61,95 @@ local function generateKeywordsForChoice(choice) addKeywords(choice, new_keywords) end +-- Helper function to create new dialog choices, returns the created choice +local function new_choice(choice_type, title, keywords) + local choice = df.adventure_conversation_choice_infost:new() + choice.cc = df.talk_choice:new() + choice.cc.type = choice_type + local text = df.new("string") + text.value = title + choice.title.text:insert("#", text) + + if keywords ~= nil then + addKeywords(choice, keywords) + end + return choice +end + +local function addWhereaboutsChoice(race, name, target_id, heard_of) + local title = "Ask for the whereabouts of the " .. race .. " " .. dfhack.TranslateName(name, true) + if heard_of then + title = title .. " (Heard of)" + end + local choice = new_choice(df.talk_choice_type.AskWhereabouts, title, dfhack.TranslateName(name):split()) + -- insert before the last choice, which is usually "back" + adventure.conversation.conv_choice_info:insert(#adventure.conversation.conv_choice_info-1, choice) + choice.cc.invocation_target_hfid = target_id +end + +local function addHistFigWhereaboutsChoice(profile) + local histfig = df.historical_figure.find(profile.histfig_id) + local race = "" + local creature = df.creature_raw.find(histfig.race) + if creature then + local caste = creature.caste[histfig.caste] + race = caste.caste_name[0] + end + + addWhereaboutsChoice(race, histfig.name, histfig.id, profile._type == df.relationship_profile_hf_historicalst) +end + +local function addIdentityWhereaboutsChoice(identity) + local identity_name = identity.name + local race = "" + local creature = df.creature_raw.find(identity.race) + if creature then + local caste = creature.caste[identity.caste] + race = caste.caste_name[0] + else + -- no race given for the identity, assume it's the histfig + local histfig = df.historical_figure.find(identity.histfig_id) + creature = df.creature_raw.find(histfig.race) + if creature then + local caste = creature.caste[histfig.caste] + race = caste.caste_name[0] + end + end + addWhereaboutsChoice(race, identity_name, identity.impersonated_hf) +end + -- Condense the rumor system choices local function rumorUpdate() + local conversation_state = adventure.conversation.conv_act.events[0].menu + -- add new conversation options depending on state + + -- If we're asking about directions, add ability to ask about all our relationships - visual, historical and identities. + -- In vanilla, we're only allowed to ask for directions to people we learned in anything that's added to df.global.adventure.rumor + if conversation_state == df.conversation_state_type.AskDirections then + local adventurer_figure = df.historical_figure.find(dfhack.world.getAdventurer().hist_figure_id) + local relationships = adventurer_figure.info.relationships + + local visual = relationships.hf_visual + local historical = relationships.hf_historical + local identity = relationships.hf_identity + + for _, profile in ipairs(visual) do + addHistFigWhereaboutsChoice(profile) + end + + -- This option will likely always fail unless the false identity is impersonating someone + -- but giving away the false identity's true historical figure feels cheap. + for _, profile in ipairs(identity) do + addIdentityWhereaboutsChoice(df.identity.find(profile.identity_id)) + end + + -- Historical entities go last so as to not give away fake identities + for _, profile in ipairs(historical) do + addHistFigWhereaboutsChoice(profile) + end + end + + -- generate extra keywords for all options for i, choice in ipairs(adventure.conversation.conv_choice_info) do generateKeywordsForChoice(choice) end @@ -78,6 +165,11 @@ AdvRumorsOverlay.ATTRS{ frame={w=0, h=0}, } +local last_first_entry = nil function AdvRumorsOverlay:render() + -- Only update if the first entry pointer changed, this reliably indicates the list changed + if #adventure.conversation.conv_choice_info <= 0 or last_first_entry == adventure.conversation.conv_choice_info[0] then return end + -- Remember the last first entry. This entry changes even if we quit out and return on the same menu! + last_first_entry = adventure.conversation.conv_choice_info[0] rumorUpdate() end